Commit c3dc4c09 authored by Don Skidmore's avatar Don Skidmore Committed by Jeff Kirsher

ixgbe: add X550 support for external PHY and forced 1G/10G support

This patch adds x550 external PHY interrupt and forced 1G/10G support.
This included enabling and handling LSC and thermal sensor interrupt.
ixgbe_handle_lasi() has been added for handling the interrupts received
over SDP0 from the external 10baseT PHY. ixgbe_enable_lasi_ext_t_x550em
and ixgbe_get_lasi_ext_t_x550em have been added to X550em to enable
mask and check interrupt flags for the external PHY.

Forced 1G/10G link speed is handled via ixgbe_mac_link_t_X550em.
ixgbe_seupt_mac_link_t_X550em sets up the internal PHY and external PHY
to either iXFI (10G) or KX (1G) based on the user selected auto
advertised link speed setting. Then sets up the external PHY auto
advertised link speed.
Signed-off-by: default avatarDon Skidmore <donald.c.skidmore@intel.com>
Tested-by: default avatarKrishneil Singh <krishneil.k.singh@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent ab5fe0c5
...@@ -1305,6 +1305,8 @@ struct ixgbe_thermal_sensor_data { ...@@ -1305,6 +1305,8 @@ struct ixgbe_thermal_sensor_data {
#define IXGBE_MDIO_AUTO_NEG_CONTROL 0x0 /* AUTO_NEG Control Reg */ #define IXGBE_MDIO_AUTO_NEG_CONTROL 0x0 /* AUTO_NEG Control Reg */
#define IXGBE_MDIO_AUTO_NEG_STATUS 0x1 /* AUTO_NEG Status Reg */ #define IXGBE_MDIO_AUTO_NEG_STATUS 0x1 /* AUTO_NEG Status Reg */
#define IXGBE_MDIO_AUTO_NEG_VENDOR_STAT 0xC800 /* AUTO_NEG Vendor Status Reg */ #define IXGBE_MDIO_AUTO_NEG_VENDOR_STAT 0xC800 /* AUTO_NEG Vendor Status Reg */
#define IXGBE_MDIO_AUTO_NEG_VENDOR_TX_ALARM2 0xCC01 /* AUTO_NEG Vendor Tx Reg */
#define IXGBE_MDIO_AUTO_NEG_VEN_LSC 0x1 /* AUTO_NEG Vendor Tx LSC */
#define IXGBE_MDIO_AUTO_NEG_ADVT 0x10 /* AUTO_NEG Advt Reg */ #define IXGBE_MDIO_AUTO_NEG_ADVT 0x10 /* AUTO_NEG Advt Reg */
#define IXGBE_MDIO_AUTO_NEG_LP 0x13 /* AUTO_NEG LP Status Reg */ #define IXGBE_MDIO_AUTO_NEG_LP 0x13 /* AUTO_NEG LP Status Reg */
#define IXGBE_MDIO_AUTO_NEG_EEE_ADVT 0x3C /* AUTO_NEG EEE Advt Reg */ #define IXGBE_MDIO_AUTO_NEG_EEE_ADVT 0x3C /* AUTO_NEG EEE Advt Reg */
...@@ -1315,10 +1317,25 @@ struct ixgbe_thermal_sensor_data { ...@@ -1315,10 +1317,25 @@ struct ixgbe_thermal_sensor_data {
#define IXGBE_MDIO_TX_VENDOR_ALARMS_3_RST_MASK 0x3 /* PHY Reset Complete Mask */ #define IXGBE_MDIO_TX_VENDOR_ALARMS_3_RST_MASK 0x3 /* PHY Reset Complete Mask */
#define IXGBE_MDIO_GLOBAL_RES_PR_10 0xC479 /* Global Resv Provisioning 10 Reg */ #define IXGBE_MDIO_GLOBAL_RES_PR_10 0xC479 /* Global Resv Provisioning 10 Reg */
#define IXGBE_MDIO_POWER_UP_STALL 0x8000 /* Power Up Stall */ #define IXGBE_MDIO_POWER_UP_STALL 0x8000 /* Power Up Stall */
#define IXGBE_MDIO_GLOBAL_INT_CHIP_STD_MASK 0xFF00 /* int std mask */
#define IXGBE_MDIO_GLOBAL_CHIP_STD_INT_FLAG 0xFC00 /* chip std int flag */
#define IXGBE_MDIO_GLOBAL_INT_CHIP_VEN_MASK 0xFF01 /* int chip-wide mask */
#define IXGBE_MDIO_GLOBAL_INT_CHIP_VEN_FLAG 0xFC01 /* int chip-wide mask */
#define IXGBE_MDIO_GLOBAL_ALARM_1 0xCC00 /* Global alarm 1 */
#define IXGBE_MDIO_GLOBAL_ALM_1_HI_TMP_FAIL 0x4000 /* high temp failure */
#define IXGBE_MDIO_GLOBAL_INT_MASK 0xD400 /* Global int mask */
/* autoneg vendor alarm int enable */
#define IXGBE_MDIO_GLOBAL_AN_VEN_ALM_INT_EN 0x1000
#define IXGBE_MDIO_GLOBAL_ALARM_1_INT 0x4 /* int in Global alarm 1 */
#define IXGBE_MDIO_GLOBAL_VEN_ALM_INT_EN 0x1 /* vendor alarm int enable */
#define IXGBE_MDIO_GLOBAL_STD_ALM2_INT 0x200 /* vendor alarm2 int mask */
#define IXGBE_MDIO_GLOBAL_INT_HI_TEMP_EN 0x4000 /* int high temp enable */
#define IXGBE_MDIO_PMA_PMD_SDA_SCL_ADDR 0xC30A /* PHY_XS SDA/SCL Addr Reg */ #define IXGBE_MDIO_PMA_PMD_SDA_SCL_ADDR 0xC30A /* PHY_XS SDA/SCL Addr Reg */
#define IXGBE_MDIO_PMA_PMD_SDA_SCL_DATA 0xC30B /* PHY_XS SDA/SCL Data Reg */ #define IXGBE_MDIO_PMA_PMD_SDA_SCL_DATA 0xC30B /* PHY_XS SDA/SCL Data Reg */
#define IXGBE_MDIO_PMA_PMD_SDA_SCL_STAT 0xC30C /* PHY_XS SDA/SCL Stat Reg */ #define IXGBE_MDIO_PMA_PMD_SDA_SCL_STAT 0xC30C /* PHY_XS SDA/SCL Stat Reg */
#define IXGBE_MDIO_PMA_TX_VEN_LASI_INT_MASK 0xD401 /* PHY TX Vendor LASI */
#define IXGBE_MDIO_PMA_TX_VEN_LASI_INT_EN 0x1 /* PHY TX Vendor LASI enable */
#define IXGBE_MDIO_PMD_STD_TX_DISABLE_CNTR 0x9 /* Standard Tx Dis Reg */ #define IXGBE_MDIO_PMD_STD_TX_DISABLE_CNTR 0x9 /* Standard Tx Dis Reg */
#define IXGBE_MDIO_PMD_GLOBAL_TX_DISABLE 0x0001 /* PMD Global Tx Dis */ #define IXGBE_MDIO_PMD_GLOBAL_TX_DISABLE 0x0001 /* PMD Global Tx Dis */
...@@ -3280,6 +3297,7 @@ struct ixgbe_phy_operations { ...@@ -3280,6 +3297,7 @@ struct ixgbe_phy_operations {
s32 (*write_i2c_combined)(struct ixgbe_hw *, u8 addr, u16 reg, u16 val); s32 (*write_i2c_combined)(struct ixgbe_hw *, u8 addr, u16 reg, u16 val);
s32 (*check_overtemp)(struct ixgbe_hw *); s32 (*check_overtemp)(struct ixgbe_hw *);
s32 (*set_phy_power)(struct ixgbe_hw *, bool on); s32 (*set_phy_power)(struct ixgbe_hw *, bool on);
s32 (*handle_lasi)(struct ixgbe_hw *hw);
}; };
struct ixgbe_eeprom_info { struct ixgbe_eeprom_info {
...@@ -3341,6 +3359,7 @@ struct ixgbe_phy_info { ...@@ -3341,6 +3359,7 @@ struct ixgbe_phy_info {
bool multispeed_fiber; bool multispeed_fiber;
bool reset_if_overtemp; bool reset_if_overtemp;
bool qsfp_shared_i2c_bus; bool qsfp_shared_i2c_bus;
u32 nw_mng_if_sel;
}; };
#include "ixgbe_mbx.h" #include "ixgbe_mbx.h"
...@@ -3509,4 +3528,6 @@ struct ixgbe_info { ...@@ -3509,4 +3528,6 @@ struct ixgbe_info {
#define IXGBE_SB_IOSF_TARGET_KX4_PCS0 2 #define IXGBE_SB_IOSF_TARGET_KX4_PCS0 2
#define IXGBE_SB_IOSF_TARGET_KX4_PCS1 3 #define IXGBE_SB_IOSF_TARGET_KX4_PCS1 3
#define IXGBE_NW_MNG_IF_SEL 0x00011178
#define IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE BIT(24)
#endif /* _IXGBE_TYPE_H_ */ #endif /* _IXGBE_TYPE_H_ */
...@@ -713,6 +713,207 @@ static s32 ixgbe_write_ee_hostif_buffer_X550(struct ixgbe_hw *hw, ...@@ -713,6 +713,207 @@ static s32 ixgbe_write_ee_hostif_buffer_X550(struct ixgbe_hw *hw,
return status; return status;
} }
/** ixgbe_write_iosf_sb_reg_x550 - Writes a value to specified register of the
* IOSF device
*
* @hw: pointer to hardware structure
* @reg_addr: 32 bit PHY register to write
* @device_type: 3 bit device type
* @data: Data to write to the register
**/
static s32 ixgbe_write_iosf_sb_reg_x550(struct ixgbe_hw *hw, u32 reg_addr,
u32 device_type, u32 data)
{
u32 gssr = IXGBE_GSSR_PHY1_SM | IXGBE_GSSR_PHY0_SM;
u32 command, error;
s32 ret;
ret = hw->mac.ops.acquire_swfw_sync(hw, gssr);
if (ret)
return ret;
ret = ixgbe_iosf_wait(hw, NULL);
if (ret)
goto out;
command = ((reg_addr << IXGBE_SB_IOSF_CTRL_ADDR_SHIFT) |
(device_type << IXGBE_SB_IOSF_CTRL_TARGET_SELECT_SHIFT));
/* Write IOSF control register */
IXGBE_WRITE_REG(hw, IXGBE_SB_IOSF_INDIRECT_CTRL, command);
/* Write IOSF data register */
IXGBE_WRITE_REG(hw, IXGBE_SB_IOSF_INDIRECT_DATA, data);
ret = ixgbe_iosf_wait(hw, &command);
if ((command & IXGBE_SB_IOSF_CTRL_RESP_STAT_MASK) != 0) {
error = (command & IXGBE_SB_IOSF_CTRL_CMPL_ERR_MASK) >>
IXGBE_SB_IOSF_CTRL_CMPL_ERR_SHIFT;
hw_dbg(hw, "Failed to write, error %x\n", error);
return IXGBE_ERR_PHY;
}
out:
hw->mac.ops.release_swfw_sync(hw, gssr);
return ret;
}
/** ixgbe_setup_ixfi_x550em - Configure the KR PHY for iXFI mode.
* @hw: pointer to hardware structure
* @speed: the link speed to force
*
* Configures the integrated KR PHY to use iXFI mode. Used to connect an
* internal and external PHY at a specific speed, without autonegotiation.
**/
static s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed)
{
s32 status;
u32 reg_val;
/* Disable AN and force speed to 10G Serial. */
status = ixgbe_read_iosf_sb_reg_x550(hw,
IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val);
if (status)
return status;
reg_val &= ~IXGBE_KRM_LINK_CTRL_1_TETH_AN_ENABLE;
reg_val &= ~IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_MASK;
/* Select forced link speed for internal PHY. */
switch (*speed) {
case IXGBE_LINK_SPEED_10GB_FULL:
reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_10G;
break;
case IXGBE_LINK_SPEED_1GB_FULL:
reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_1G;
break;
default:
/* Other link speeds are not supported by internal KR PHY. */
return IXGBE_ERR_LINK_SETUP;
}
status = ixgbe_write_iosf_sb_reg_x550(hw,
IXGBE_KRM_RX_TRN_LINKUP_CTRL(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val);
if (status)
return status;
/* Disable training protocol FSM. */
status = ixgbe_read_iosf_sb_reg_x550(hw,
IXGBE_KRM_RX_TRN_LINKUP_CTRL(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val);
if (status)
return status;
reg_val |= IXGBE_KRM_RX_TRN_LINKUP_CTRL_CONV_WO_PROTOCOL;
status = ixgbe_write_iosf_sb_reg_x550(hw,
IXGBE_KRM_RX_TRN_LINKUP_CTRL(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val);
if (status)
return status;
/* Disable Flex from training TXFFE. */
status = ixgbe_read_iosf_sb_reg_x550(hw,
IXGBE_KRM_DSP_TXFFE_STATE_4(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val);
if (status)
return status;
reg_val &= ~IXGBE_KRM_DSP_TXFFE_STATE_C0_EN;
reg_val &= ~IXGBE_KRM_DSP_TXFFE_STATE_CP1_CN1_EN;
reg_val &= ~IXGBE_KRM_DSP_TXFFE_STATE_CO_ADAPT_EN;
status = ixgbe_write_iosf_sb_reg_x550(hw,
IXGBE_KRM_DSP_TXFFE_STATE_4(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val);
if (status)
return status;
status = ixgbe_read_iosf_sb_reg_x550(hw,
IXGBE_KRM_DSP_TXFFE_STATE_5(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val);
if (status)
return status;
reg_val &= ~IXGBE_KRM_DSP_TXFFE_STATE_C0_EN;
reg_val &= ~IXGBE_KRM_DSP_TXFFE_STATE_CP1_CN1_EN;
reg_val &= ~IXGBE_KRM_DSP_TXFFE_STATE_CO_ADAPT_EN;
status = ixgbe_write_iosf_sb_reg_x550(hw,
IXGBE_KRM_DSP_TXFFE_STATE_5(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val);
if (status)
return status;
/* Enable override for coefficients. */
status = ixgbe_read_iosf_sb_reg_x550(hw,
IXGBE_KRM_TX_COEFF_CTRL_1(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val);
if (status)
return status;
reg_val |= IXGBE_KRM_TX_COEFF_CTRL_1_OVRRD_EN;
reg_val |= IXGBE_KRM_TX_COEFF_CTRL_1_CZERO_EN;
reg_val |= IXGBE_KRM_TX_COEFF_CTRL_1_CPLUS1_OVRRD_EN;
reg_val |= IXGBE_KRM_TX_COEFF_CTRL_1_CMINUS1_OVRRD_EN;
status = ixgbe_write_iosf_sb_reg_x550(hw,
IXGBE_KRM_TX_COEFF_CTRL_1(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val);
if (status)
return status;
/* Toggle port SW reset by AN reset. */
status = ixgbe_read_iosf_sb_reg_x550(hw,
IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val);
if (status)
return status;
reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_RESTART;
status = ixgbe_write_iosf_sb_reg_x550(hw,
IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val);
return status;
}
/**
* ixgbe_setup_mac_link_t_X550em - Sets the auto advertised link speed
* @hw: pointer to hardware structure
* @speed: new link speed
* @autoneg_wait_to_complete: true when waiting for completion is needed
*
* Setup internal/external PHY link speed based on link speed, then set
* external PHY auto advertised link speed.
*
* Returns error status for any failure
**/
static s32 ixgbe_setup_mac_link_t_X550em(struct ixgbe_hw *hw,
ixgbe_link_speed speed,
bool autoneg_wait)
{
s32 status;
ixgbe_link_speed force_speed;
/* Setup internal/external PHY link speed to iXFI (10G), unless
* only 1G is auto advertised then setup KX link.
*/
if (speed & IXGBE_LINK_SPEED_10GB_FULL)
force_speed = IXGBE_LINK_SPEED_10GB_FULL;
else
force_speed = IXGBE_LINK_SPEED_1GB_FULL;
/* If internal link mode is XFI, then setup XFI internal link. */
if (!(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE)) {
status = ixgbe_setup_ixfi_x550em(hw, &force_speed);
if (status)
return status;
}
return hw->phy.ops.setup_link_speed(hw, speed, autoneg_wait);
}
/** ixgbe_init_mac_link_ops_X550em - init mac link function pointers /** ixgbe_init_mac_link_ops_X550em - init mac link function pointers
* @hw: pointer to hardware structure * @hw: pointer to hardware structure
**/ **/
...@@ -720,13 +921,20 @@ static void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw) ...@@ -720,13 +921,20 @@ static void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw)
{ {
struct ixgbe_mac_info *mac = &hw->mac; struct ixgbe_mac_info *mac = &hw->mac;
switch (mac->ops.get_media_type(hw)) {
case ixgbe_media_type_fiber:
/* CS4227 does not support autoneg, so disable the laser control /* CS4227 does not support autoneg, so disable the laser control
* functions for SFP+ fiber * functions for SFP+ fiber
*/ */
if (hw->device_id == IXGBE_DEV_ID_X550EM_X_SFP) {
mac->ops.disable_tx_laser = NULL; mac->ops.disable_tx_laser = NULL;
mac->ops.enable_tx_laser = NULL; mac->ops.enable_tx_laser = NULL;
mac->ops.flap_tx_laser = NULL; mac->ops.flap_tx_laser = NULL;
break;
case ixgbe_media_type_copper:
mac->ops.setup_link = ixgbe_setup_mac_link_t_X550em;
break;
default:
break;
} }
} }
...@@ -818,162 +1026,217 @@ static s32 ixgbe_get_link_capabilities_X550em(struct ixgbe_hw *hw, ...@@ -818,162 +1026,217 @@ static s32 ixgbe_get_link_capabilities_X550em(struct ixgbe_hw *hw,
return 0; return 0;
} }
/** ixgbe_write_iosf_sb_reg_x550 - Writes a value to specified register of the /**
* IOSF device * ixgbe_get_lasi_ext_t_x550em - Determime external Base T PHY interrupt cause
*
* @hw: pointer to hardware structure * @hw: pointer to hardware structure
* @reg_addr: 32 bit PHY register to write * @lsc: pointer to boolean flag which indicates whether external Base T
* @device_type: 3 bit device type * PHY interrupt is lsc
* @data: Data to write to the register *
* Determime if external Base T PHY interrupt cause is high temperature
* failure alarm or link status change.
*
* Return IXGBE_ERR_OVERTEMP if interrupt is high temperature
* failure alarm, else return PHY access status.
**/ **/
static s32 ixgbe_write_iosf_sb_reg_x550(struct ixgbe_hw *hw, u32 reg_addr, static s32 ixgbe_get_lasi_ext_t_x550em(struct ixgbe_hw *hw, bool *lsc)
u32 device_type, u32 data)
{ {
u32 gssr = IXGBE_GSSR_PHY1_SM | IXGBE_GSSR_PHY0_SM; u32 status;
u32 command, error; u16 reg;
s32 ret;
ret = hw->mac.ops.acquire_swfw_sync(hw, gssr); *lsc = false;
if (ret)
return ret;
ret = ixgbe_iosf_wait(hw, NULL); /* Vendor alarm triggered */
if (ret) status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_CHIP_STD_INT_FLAG,
goto out; IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
&reg);
command = ((reg_addr << IXGBE_SB_IOSF_CTRL_ADDR_SHIFT) | if (status || !(reg & IXGBE_MDIO_GLOBAL_VEN_ALM_INT_EN))
(device_type << IXGBE_SB_IOSF_CTRL_TARGET_SELECT_SHIFT)); return status;
/* Write IOSF control register */ /* Vendor Auto-Neg alarm triggered or Global alarm 1 triggered */
IXGBE_WRITE_REG(hw, IXGBE_SB_IOSF_INDIRECT_CTRL, command); status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_INT_CHIP_VEN_FLAG,
IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
&reg);
/* Write IOSF data register */ if (status || !(reg & (IXGBE_MDIO_GLOBAL_AN_VEN_ALM_INT_EN |
IXGBE_WRITE_REG(hw, IXGBE_SB_IOSF_INDIRECT_DATA, data); IXGBE_MDIO_GLOBAL_ALARM_1_INT)))
return status;
ret = ixgbe_iosf_wait(hw, &command); /* High temperature failure alarm triggered */
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_ALARM_1,
IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
&reg);
if ((command & IXGBE_SB_IOSF_CTRL_RESP_STAT_MASK) != 0) { if (status)
error = (command & IXGBE_SB_IOSF_CTRL_CMPL_ERR_MASK) >> return status;
IXGBE_SB_IOSF_CTRL_CMPL_ERR_SHIFT;
hw_dbg(hw, "Failed to write, error %x\n", error); /* If high temperature failure, then return over temp error and exit */
return IXGBE_ERR_PHY; if (reg & IXGBE_MDIO_GLOBAL_ALM_1_HI_TMP_FAIL) {
/* power down the PHY in case the PHY FW didn't already */
ixgbe_set_copper_phy_power(hw, false);
return IXGBE_ERR_OVERTEMP;
} }
out: /* Vendor alarm 2 triggered */
hw->mac.ops.release_swfw_sync(hw, gssr); status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_CHIP_STD_INT_FLAG,
return ret; IXGBE_MDIO_AUTO_NEG_DEV_TYPE, &reg);
if (status || !(reg & IXGBE_MDIO_GLOBAL_STD_ALM2_INT))
return status;
/* link connect/disconnect event occurred */
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_VENDOR_TX_ALARM2,
IXGBE_MDIO_AUTO_NEG_DEV_TYPE, &reg);
if (status)
return status;
/* Indicate LSC */
if (reg & IXGBE_MDIO_AUTO_NEG_VEN_LSC)
*lsc = true;
return 0;
} }
/** ixgbe_setup_ixfi_x550em - Configure the KR PHY for iXFI mode. /**
* ixgbe_enable_lasi_ext_t_x550em - Enable external Base T PHY interrupts
* @hw: pointer to hardware structure * @hw: pointer to hardware structure
* @speed: the link speed to force
* *
* Configures the integrated KR PHY to use iXFI mode. Used to connect an * Enable link status change and temperature failure alarm for the external
* internal and external PHY at a specific speed, without autonegotiation. * Base T PHY
*
* Returns PHY access status
**/ **/
static s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed) static s32 ixgbe_enable_lasi_ext_t_x550em(struct ixgbe_hw *hw)
{ {
s32 status; u32 status;
u32 reg_val; u16 reg;
bool lsc;
/* Disable AN and force speed to 10G Serial. */ /* Clear interrupt flags */
status = ixgbe_read_iosf_sb_reg_x550(hw, status = ixgbe_get_lasi_ext_t_x550em(hw, &lsc);
IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val); /* Enable link status change alarm */
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_PMA_TX_VEN_LASI_INT_MASK,
IXGBE_MDIO_AUTO_NEG_DEV_TYPE, &reg);
if (status) if (status)
return status; return status;
reg_val &= ~IXGBE_KRM_LINK_CTRL_1_TETH_AN_ENABLE; reg |= IXGBE_MDIO_PMA_TX_VEN_LASI_INT_EN;
reg_val &= ~IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_MASK;
/* Select forced link speed for internal PHY. */
switch (*speed) {
case IXGBE_LINK_SPEED_10GB_FULL:
reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_10G;
break;
case IXGBE_LINK_SPEED_1GB_FULL:
reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_1G;
break;
default:
/* Other link speeds are not supported by internal KR PHY. */
return IXGBE_ERR_LINK_SETUP;
}
status = ixgbe_write_iosf_sb_reg_x550(hw, status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_PMA_TX_VEN_LASI_INT_MASK,
IXGBE_KRM_RX_TRN_LINKUP_CTRL(hw->bus.lan_id), IXGBE_MDIO_AUTO_NEG_DEV_TYPE, reg);
IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val);
if (status) if (status)
return status; return status;
/* Disable training protocol FSM. */ /* Enables high temperature failure alarm */
status = ixgbe_read_iosf_sb_reg_x550(hw, status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_INT_MASK,
IXGBE_KRM_RX_TRN_LINKUP_CTRL(hw->bus.lan_id), IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val); &reg);
if (status) if (status)
return status; return status;
reg_val |= IXGBE_KRM_RX_TRN_LINKUP_CTRL_CONV_WO_PROTOCOL; reg |= IXGBE_MDIO_GLOBAL_INT_HI_TEMP_EN;
status = ixgbe_write_iosf_sb_reg_x550(hw,
IXGBE_KRM_RX_TRN_LINKUP_CTRL(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val);
if (status)
return status;
/* Disable Flex from training TXFFE. */ status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_GLOBAL_INT_MASK,
status = ixgbe_read_iosf_sb_reg_x550(hw, IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
IXGBE_KRM_DSP_TXFFE_STATE_4(hw->bus.lan_id), reg);
IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val);
if (status) if (status)
return status; return status;
reg_val &= ~IXGBE_KRM_DSP_TXFFE_STATE_C0_EN; /* Enable vendor Auto-Neg alarm and Global Interrupt Mask 1 alarm */
reg_val &= ~IXGBE_KRM_DSP_TXFFE_STATE_CP1_CN1_EN; status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_INT_CHIP_VEN_MASK,
reg_val &= ~IXGBE_KRM_DSP_TXFFE_STATE_CO_ADAPT_EN; IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
status = ixgbe_write_iosf_sb_reg_x550(hw, &reg);
IXGBE_KRM_DSP_TXFFE_STATE_4(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val);
if (status) if (status)
return status; return status;
status = ixgbe_read_iosf_sb_reg_x550(hw, reg |= (IXGBE_MDIO_GLOBAL_AN_VEN_ALM_INT_EN |
IXGBE_KRM_DSP_TXFFE_STATE_5(hw->bus.lan_id), IXGBE_MDIO_GLOBAL_ALARM_1_INT);
IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val);
status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_GLOBAL_INT_CHIP_VEN_MASK,
IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
reg);
if (status) if (status)
return status; return status;
reg_val &= ~IXGBE_KRM_DSP_TXFFE_STATE_C0_EN; /* Enable chip-wide vendor alarm */
reg_val &= ~IXGBE_KRM_DSP_TXFFE_STATE_CP1_CN1_EN; status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_INT_CHIP_STD_MASK,
reg_val &= ~IXGBE_KRM_DSP_TXFFE_STATE_CO_ADAPT_EN; IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
status = ixgbe_write_iosf_sb_reg_x550(hw, &reg);
IXGBE_KRM_DSP_TXFFE_STATE_5(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val);
if (status) if (status)
return status; return status;
/* Enable override for coefficients. */ reg |= IXGBE_MDIO_GLOBAL_VEN_ALM_INT_EN;
status = ixgbe_read_iosf_sb_reg_x550(hw,
IXGBE_KRM_TX_COEFF_CTRL_1(hw->bus.lan_id), status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_GLOBAL_INT_CHIP_STD_MASK,
IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val); IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
if (status) reg);
return status; return status;
}
reg_val |= IXGBE_KRM_TX_COEFF_CTRL_1_OVRRD_EN; /**
reg_val |= IXGBE_KRM_TX_COEFF_CTRL_1_CZERO_EN; * ixgbe_handle_lasi_ext_t_x550em - Handle external Base T PHY interrupt
reg_val |= IXGBE_KRM_TX_COEFF_CTRL_1_CPLUS1_OVRRD_EN; * @hw: pointer to hardware structure
reg_val |= IXGBE_KRM_TX_COEFF_CTRL_1_CMINUS1_OVRRD_EN; *
status = ixgbe_write_iosf_sb_reg_x550(hw, * Handle external Base T PHY interrupt. If high temperature
IXGBE_KRM_TX_COEFF_CTRL_1(hw->bus.lan_id), * failure alarm then return error, else if link status change
IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val); * then setup internal/external PHY link
*
* Return IXGBE_ERR_OVERTEMP if interrupt is high temperature
* failure alarm, else return PHY access status.
**/
static s32 ixgbe_handle_lasi_ext_t_x550em(struct ixgbe_hw *hw)
{
struct ixgbe_phy_info *phy = &hw->phy;
bool lsc;
u32 status;
status = ixgbe_get_lasi_ext_t_x550em(hw, &lsc);
if (status) if (status)
return status; return status;
/* Toggle port SW reset by AN reset. */ if (lsc)
return phy->ops.setup_internal_link(hw);
return 0;
}
/**
* ixgbe_setup_kr_speed_x550em - Configure the KR PHY for link speed.
* @hw: pointer to hardware structure
* @speed: link speed
*
* Configures the integrated KR PHY.
**/
static s32 ixgbe_setup_kr_speed_x550em(struct ixgbe_hw *hw,
ixgbe_link_speed speed)
{
s32 status;
u32 reg_val;
status = ixgbe_read_iosf_sb_reg_x550(hw, status = ixgbe_read_iosf_sb_reg_x550(hw,
IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val); IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val);
if (status) if (status)
return status; return status;
reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_ENABLE;
reg_val &= ~(IXGBE_KRM_LINK_CTRL_1_TETH_AN_FEC_REQ |
IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_FEC);
reg_val &= ~(IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_KR |
IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_KX);
/* Advertise 10G support. */
if (speed & IXGBE_LINK_SPEED_10GB_FULL)
reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_KR;
/* Advertise 1G support. */
if (speed & IXGBE_LINK_SPEED_1GB_FULL)
reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_KX;
/* Restart auto-negotiation. */
reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_RESTART; reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_RESTART;
status = ixgbe_write_iosf_sb_reg_x550(hw, status = ixgbe_write_iosf_sb_reg_x550(hw,
IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
...@@ -1027,85 +1290,82 @@ static s32 ixgbe_setup_kx4_x550em(struct ixgbe_hw *hw) ...@@ -1027,85 +1290,82 @@ static s32 ixgbe_setup_kx4_x550em(struct ixgbe_hw *hw)
**/ **/
static s32 ixgbe_setup_kr_x550em(struct ixgbe_hw *hw) static s32 ixgbe_setup_kr_x550em(struct ixgbe_hw *hw)
{ {
s32 status; return ixgbe_setup_kr_speed_x550em(hw, hw->phy.autoneg_advertised);
u32 reg_val; }
status = ixgbe_read_iosf_sb_reg_x550(hw, /** ixgbe_ext_phy_t_x550em_get_link - Get ext phy link status
IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), * @hw: address of hardware structure
IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val); * @link_up: address of boolean to indicate link status
if (status) *
return status; * Returns error code if unable to get link status.
**/
static s32 ixgbe_ext_phy_t_x550em_get_link(struct ixgbe_hw *hw, bool *link_up)
{
u32 ret;
u16 autoneg_status;
reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_ENABLE; *link_up = false;
reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_FEC_REQ;
reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_FEC;
reg_val &= ~(IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_KR |
IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_KX);
/* Advertise 10G support. */ /* read this twice back to back to indicate current status */
if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL) ret = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_STATUS,
reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_KR; IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
&autoneg_status);
if (ret)
return ret;
/* Advertise 1G support. */ ret = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_STATUS,
if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL) IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_KX; &autoneg_status);
if (ret)
return ret;
/* Restart auto-negotiation. */ *link_up = !!(autoneg_status & IXGBE_MDIO_AUTO_NEG_LINK_STATUS);
reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_RESTART;
status = ixgbe_write_iosf_sb_reg_x550(hw,
IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val);
return status; return 0;
} }
/** ixgbe_setup_internal_phy_x550em - Configure integrated KR PHY /** ixgbe_setup_internal_phy_t_x550em - Configure KR PHY to X557 link
* @hw: point to hardware structure * @hw: point to hardware structure
* *
* Configures the integrated KR PHY to talk to the external PHY. The base * Configures the link between the integrated KR PHY and the external X557 PHY
* driver will call this function when it gets notification via interrupt from * The driver will call this function when it gets a link status change
* the external PHY. This function forces the internal PHY into iXFI mode at * interrupt from the X557 PHY. This function configures the link speed
* the correct speed. * between the PHYs to match the link speed of the BASE-T link.
* *
* A return of a non-zero value indicates an error, and the base driver should * A return of a non-zero value indicates an error, and the base driver should
* not report link up. * not report link up.
**/ **/
static s32 ixgbe_setup_internal_phy_x550em(struct ixgbe_hw *hw) static s32 ixgbe_setup_internal_phy_t_x550em(struct ixgbe_hw *hw)
{ {
s32 status;
u16 lasi, autoneg_status, speed;
ixgbe_link_speed force_speed; ixgbe_link_speed force_speed;
bool link_up;
u32 status;
u16 speed;
if (hw->mac.ops.get_media_type(hw) != ixgbe_media_type_copper)
return IXGBE_ERR_CONFIG;
/* Verify that the external link status has changed */ /* If link is not up, then there is no setup necessary so return */
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_XENPAK_LASI_STATUS, status = ixgbe_ext_phy_t_x550em_get_link(hw, &link_up);
IXGBE_MDIO_PMA_PMD_DEV_TYPE, &lasi);
if (status) if (status)
return status; return status;
/* If there was no change in link status, we can just exit */ if (!link_up)
if (!(lasi & IXGBE_XENPAK_LASI_LINK_STATUS_ALARM))
return 0; return 0;
/* we read this twice back to back to indicate current status */ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_VENDOR_STAT,
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_STATUS,
IXGBE_MDIO_AUTO_NEG_DEV_TYPE, IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
&autoneg_status); &speed);
if (status) if (status)
return status; return status;
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_STATUS, /* If link is not still up, then no setup is necessary so return */
IXGBE_MDIO_AUTO_NEG_DEV_TYPE, status = ixgbe_ext_phy_t_x550em_get_link(hw, &link_up);
&autoneg_status);
if (status) if (status)
return status; return status;
/* If link is not up return an error indicating treat link as down */ if (!link_up)
if (!(autoneg_status & IXGBE_MDIO_AUTO_NEG_LINK_STATUS)) return 0;
return IXGBE_ERR_INVALID_LINK_SETTINGS;
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_VENDOR_STAT,
IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
&speed);
/* clear everything but the speed and duplex bits */ /* clear everything but the speed and duplex bits */
speed &= IXGBE_MDIO_AUTO_NEG_VENDOR_STATUS_MASK; speed &= IXGBE_MDIO_AUTO_NEG_VENDOR_STATUS_MASK;
...@@ -1135,17 +1395,30 @@ static s32 ixgbe_setup_internal_phy_x550em(struct ixgbe_hw *hw) ...@@ -1135,17 +1395,30 @@ static s32 ixgbe_setup_internal_phy_x550em(struct ixgbe_hw *hw)
static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw) static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw)
{ {
struct ixgbe_phy_info *phy = &hw->phy; struct ixgbe_phy_info *phy = &hw->phy;
ixgbe_link_speed speed;
s32 ret_val; s32 ret_val;
if (hw->device_id == IXGBE_DEV_ID_X550EM_X_SFP) { if (hw->mac.ops.get_media_type(hw) == ixgbe_media_type_fiber) {
phy->phy_semaphore_mask = IXGBE_GSSR_SHARED_I2C_SM; phy->phy_semaphore_mask = IXGBE_GSSR_SHARED_I2C_SM;
ixgbe_setup_mux_ctl(hw); ixgbe_setup_mux_ctl(hw);
/* Save NW management interface connected on board. This is used
* to determine internal PHY mode.
*/
phy->nw_mng_if_sel = IXGBE_READ_REG(hw, IXGBE_NW_MNG_IF_SEL);
/* If internal PHY mode is KR, then initialize KR link */
if (phy->nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE) {
speed = IXGBE_LINK_SPEED_10GB_FULL |
IXGBE_LINK_SPEED_1GB_FULL;
ret_val = ixgbe_setup_kr_speed_x550em(hw, speed);
}
} }
/* Identify the PHY or SFP module */ /* Identify the PHY or SFP module */
ret_val = phy->ops.identify(hw); ret_val = phy->ops.identify(hw);
/* Setup function pointers based on detected SFP module and speeds */ /* Setup function pointers based on detected hardware */
ixgbe_init_mac_link_ops_X550em(hw); ixgbe_init_mac_link_ops_X550em(hw);
if (phy->sfp_type != ixgbe_sfp_type_unknown) if (phy->sfp_type != ixgbe_sfp_type_unknown)
phy->ops.reset = NULL; phy->ops.reset = NULL;
...@@ -1163,11 +1436,29 @@ static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw) ...@@ -1163,11 +1436,29 @@ static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw)
phy->ops.write_reg = ixgbe_write_phy_reg_x550em; phy->ops.write_reg = ixgbe_write_phy_reg_x550em;
break; break;
case ixgbe_phy_x550em_ext_t: case ixgbe_phy_x550em_ext_t:
phy->ops.setup_internal_link = ixgbe_setup_internal_phy_x550em; /* Save NW management interface connected on board. This is used
* to determine internal PHY mode
*/
phy->nw_mng_if_sel = IXGBE_READ_REG(hw, IXGBE_NW_MNG_IF_SEL);
/* If internal link mode is XFI, then setup iXFI internal link,
* else setup KR now.
*/
if (!(phy->nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE)) {
phy->ops.setup_internal_link =
ixgbe_setup_internal_phy_t_x550em;
} else {
speed = IXGBE_LINK_SPEED_10GB_FULL |
IXGBE_LINK_SPEED_1GB_FULL;
ret_val = ixgbe_setup_kr_speed_x550em(hw, speed);
}
phy->ops.handle_lasi = ixgbe_handle_lasi_ext_t_x550em;
break; break;
default: default:
break; break;
} }
return ret_val; return ret_val;
} }
......
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