ixgbe: Implement support for firmware-controlled PHYs

Implement support for devices that have firmware-controlled PHYs.

Signed-off-by: Mark Rustad <mark.d.rustad@intel.com>
Tested-by: Krishneil Singh <krishneil.k.singh@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
This commit is contained in:
Mark Rustad 2016-12-14 11:02:16 -08:00 committed by Jeff Kirsher
parent 12c78ef098
commit b3eb4e1860
7 changed files with 642 additions and 6 deletions

View File

@ -661,6 +661,8 @@ struct ixgbe_adapter {
#define IXGBE_FLAG2_PHY_INTERRUPT BIT(11)
#define IXGBE_FLAG2_UDP_TUN_REREG_NEEDED BIT(12)
#define IXGBE_FLAG2_VLAN_PROMISC BIT(13)
#define IXGBE_FLAG2_EEE_CAPABLE BIT(14)
#define IXGBE_FLAG2_EEE_ENABLED BIT(15)
/* Tx fast path data */
int num_tx_queues;
@ -862,6 +864,7 @@ enum ixgbe_boards {
board_X550,
board_X550EM_x,
board_x550em_a,
board_x550em_a_fw,
};
extern const struct ixgbe_info ixgbe_82598_info;
@ -870,6 +873,7 @@ extern const struct ixgbe_info ixgbe_X540_info;
extern const struct ixgbe_info ixgbe_X550_info;
extern const struct ixgbe_info ixgbe_X550EM_x_info;
extern const struct ixgbe_info ixgbe_x550em_a_info;
extern const struct ixgbe_info ixgbe_x550em_a_fw_info;
#ifdef CONFIG_IXGBE_DCB
extern const struct dcbnl_rtnl_ops dcbnl_ops;
#endif

View File

@ -100,6 +100,8 @@ bool ixgbe_device_supports_autoneg_fc(struct ixgbe_hw *hw)
case IXGBE_DEV_ID_X550T1:
case IXGBE_DEV_ID_X550EM_X_10G_T:
case IXGBE_DEV_ID_X550EM_A_10G_T:
case IXGBE_DEV_ID_X550EM_A_1G_T:
case IXGBE_DEV_ID_X550EM_A_1G_T_L:
supported = true;
break;
default:
@ -3382,6 +3384,13 @@ s32 ixgbe_check_mac_link_generic(struct ixgbe_hw *hw, ixgbe_link_speed *speed,
else
*speed = IXGBE_LINK_SPEED_100_FULL;
break;
case IXGBE_LINKS_SPEED_10_X550EM_A:
*speed = IXGBE_LINK_SPEED_UNKNOWN;
if (hw->device_id == IXGBE_DEV_ID_X550EM_A_1G_T ||
hw->device_id == IXGBE_DEV_ID_X550EM_A_1G_T_L) {
*speed = IXGBE_LINK_SPEED_10_FULL;
}
break;
default:
*speed = IXGBE_LINK_SPEED_UNKNOWN;
}

View File

@ -197,15 +197,17 @@ static int ixgbe_get_settings(struct net_device *netdev,
SUPPORTED_1000baseKX_Full :
SUPPORTED_1000baseT_Full;
if (supported_link & IXGBE_LINK_SPEED_100_FULL)
ecmd->supported |= ixgbe_isbackplane(hw->phy.media_type) ?
SUPPORTED_1000baseKX_Full :
SUPPORTED_100baseT_Full;
ecmd->supported |= SUPPORTED_100baseT_Full;
if (supported_link & IXGBE_LINK_SPEED_10_FULL)
ecmd->supported |= SUPPORTED_10baseT_Full;
/* default advertised speed if phy.autoneg_advertised isn't set */
ecmd->advertising = ecmd->supported;
/* set the advertised speeds */
if (hw->phy.autoneg_advertised) {
ecmd->advertising = 0;
if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10_FULL)
ecmd->advertising |= ADVERTISED_10baseT_Full;
if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_100_FULL)
ecmd->advertising |= ADVERTISED_100baseT_Full;
if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL)
@ -237,6 +239,7 @@ static int ixgbe_get_settings(struct net_device *netdev,
case ixgbe_phy_tn:
case ixgbe_phy_aq:
case ixgbe_phy_x550em_ext_t:
case ixgbe_phy_fw:
case ixgbe_phy_cu_unknown:
ecmd->supported |= SUPPORTED_TP;
ecmd->advertising |= ADVERTISED_TP;
@ -346,6 +349,9 @@ static int ixgbe_get_settings(struct net_device *netdev,
case IXGBE_LINK_SPEED_100_FULL:
ethtool_cmd_speed_set(ecmd, SPEED_100);
break;
case IXGBE_LINK_SPEED_10_FULL:
ethtool_cmd_speed_set(ecmd, SPEED_10);
break;
default:
break;
}
@ -394,6 +400,9 @@ static int ixgbe_set_settings(struct net_device *netdev,
if (ecmd->advertising & ADVERTISED_100baseT_Full)
advertised |= IXGBE_LINK_SPEED_100_FULL;
if (ecmd->advertising & ADVERTISED_10baseT_Full)
advertised |= IXGBE_LINK_SPEED_10_FULL;
if (old == advertised)
return err;
/* this sets the link speed and restarts auto-neg */
@ -3173,6 +3182,9 @@ static int ixgbe_get_module_info(struct net_device *dev,
u8 sff8472_rev, addr_mode;
bool page_swap = false;
if (hw->phy.type == ixgbe_phy_fw)
return -ENXIO;
/* Check whether we support SFF-8472 or not */
status = hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_SFF_8472_COMP,
@ -3218,6 +3230,9 @@ static int ixgbe_get_module_eeprom(struct net_device *dev,
if (ee->len == 0)
return -EINVAL;
if (hw->phy.type == ixgbe_phy_fw)
return -ENXIO;
for (i = ee->offset; i < ee->offset + ee->len; i++) {
/* I2C reads can take long time */
if (test_bit(__IXGBE_IN_SFP_INIT, &adapter->state))
@ -3237,6 +3252,136 @@ static int ixgbe_get_module_eeprom(struct net_device *dev,
return 0;
}
static const struct {
ixgbe_link_speed mac_speed;
u32 supported;
} ixgbe_ls_map[] = {
{ IXGBE_LINK_SPEED_10_FULL, SUPPORTED_10baseT_Full },
{ IXGBE_LINK_SPEED_100_FULL, SUPPORTED_100baseT_Full },
{ IXGBE_LINK_SPEED_1GB_FULL, SUPPORTED_1000baseT_Full },
{ IXGBE_LINK_SPEED_2_5GB_FULL, SUPPORTED_2500baseX_Full },
{ IXGBE_LINK_SPEED_10GB_FULL, SUPPORTED_10000baseT_Full },
};
static const struct {
u32 lp_advertised;
u32 mac_speed;
} ixgbe_lp_map[] = {
{ FW_PHY_ACT_UD_2_100M_TX_EEE, SUPPORTED_100baseT_Full },
{ FW_PHY_ACT_UD_2_1G_T_EEE, SUPPORTED_1000baseT_Full },
{ FW_PHY_ACT_UD_2_10G_T_EEE, SUPPORTED_10000baseT_Full },
{ FW_PHY_ACT_UD_2_1G_KX_EEE, SUPPORTED_1000baseKX_Full },
{ FW_PHY_ACT_UD_2_10G_KX4_EEE, SUPPORTED_10000baseKX4_Full },
{ FW_PHY_ACT_UD_2_10G_KR_EEE, SUPPORTED_10000baseKR_Full},
};
static int
ixgbe_get_eee_fw(struct ixgbe_adapter *adapter, struct ethtool_eee *edata)
{
u32 info[FW_PHY_ACT_DATA_COUNT] = { 0 };
struct ixgbe_hw *hw = &adapter->hw;
s32 rc;
u16 i;
rc = ixgbe_fw_phy_activity(hw, FW_PHY_ACT_UD_2, &info);
if (rc)
return rc;
edata->lp_advertised = 0;
for (i = 0; i < ARRAY_SIZE(ixgbe_lp_map); ++i) {
if (info[0] & ixgbe_lp_map[i].lp_advertised)
edata->lp_advertised |= ixgbe_lp_map[i].mac_speed;
}
edata->supported = 0;
for (i = 0; i < ARRAY_SIZE(ixgbe_ls_map); ++i) {
if (hw->phy.eee_speeds_supported & ixgbe_ls_map[i].mac_speed)
edata->supported |= ixgbe_ls_map[i].supported;
}
edata->advertised = 0;
for (i = 0; i < ARRAY_SIZE(ixgbe_ls_map); ++i) {
if (hw->phy.eee_speeds_advertised & ixgbe_ls_map[i].mac_speed)
edata->advertised |= ixgbe_ls_map[i].supported;
}
edata->eee_enabled = !!edata->advertised;
edata->tx_lpi_enabled = edata->eee_enabled;
if (edata->advertised & edata->lp_advertised)
edata->eee_active = true;
return 0;
}
static int ixgbe_get_eee(struct net_device *netdev, struct ethtool_eee *edata)
{
struct ixgbe_adapter *adapter = netdev_priv(netdev);
struct ixgbe_hw *hw = &adapter->hw;
if (!(adapter->flags2 & IXGBE_FLAG2_EEE_CAPABLE))
return -EOPNOTSUPP;
if (hw->phy.eee_speeds_supported && hw->phy.type == ixgbe_phy_fw)
return ixgbe_get_eee_fw(adapter, edata);
return -EOPNOTSUPP;
}
static int ixgbe_set_eee(struct net_device *netdev, struct ethtool_eee *edata)
{
struct ixgbe_adapter *adapter = netdev_priv(netdev);
struct ixgbe_hw *hw = &adapter->hw;
struct ethtool_eee eee_data;
s32 ret_val;
if (!(adapter->flags2 & IXGBE_FLAG2_EEE_CAPABLE))
return -EOPNOTSUPP;
memset(&eee_data, 0, sizeof(struct ethtool_eee));
ret_val = ixgbe_get_eee(netdev, &eee_data);
if (ret_val)
return ret_val;
if (eee_data.eee_enabled && !edata->eee_enabled) {
if (eee_data.tx_lpi_enabled != edata->tx_lpi_enabled) {
e_err(drv, "Setting EEE tx-lpi is not supported\n");
return -EINVAL;
}
if (eee_data.tx_lpi_timer != edata->tx_lpi_timer) {
e_err(drv,
"Setting EEE Tx LPI timer is not supported\n");
return -EINVAL;
}
if (eee_data.advertised != edata->advertised) {
e_err(drv,
"Setting EEE advertised speeds is not supported\n");
return -EINVAL;
}
}
if (eee_data.eee_enabled != edata->eee_enabled) {
if (edata->eee_enabled) {
adapter->flags2 |= IXGBE_FLAG2_EEE_ENABLED;
hw->phy.eee_speeds_advertised =
hw->phy.eee_speeds_supported;
} else {
adapter->flags2 &= ~IXGBE_FLAG2_EEE_ENABLED;
hw->phy.eee_speeds_advertised = 0;
}
/* reset link */
if (netif_running(netdev))
ixgbe_reinit_locked(adapter);
else
ixgbe_reset(adapter);
}
return 0;
}
static const struct ethtool_ops ixgbe_ethtool_ops = {
.get_settings = ixgbe_get_settings,
.set_settings = ixgbe_set_settings,
@ -3269,6 +3414,8 @@ static const struct ethtool_ops ixgbe_ethtool_ops = {
.get_rxfh_key_size = ixgbe_get_rxfh_key_size,
.get_rxfh = ixgbe_get_rxfh,
.set_rxfh = ixgbe_set_rxfh,
.get_eee = ixgbe_get_eee,
.set_eee = ixgbe_set_eee,
.get_channels = ixgbe_get_channels,
.set_channels = ixgbe_set_channels,
.get_ts_info = ixgbe_get_ts_info,

View File

@ -86,6 +86,7 @@ static const struct ixgbe_info *ixgbe_info_tbl[] = {
[board_X550] = &ixgbe_X550_info,
[board_X550EM_x] = &ixgbe_X550EM_x_info,
[board_x550em_a] = &ixgbe_x550em_a_info,
[board_x550em_a_fw] = &ixgbe_x550em_a_fw_info,
};
/* ixgbe_pci_tbl - PCI Device ID Table
@ -140,6 +141,8 @@ static const struct pci_device_id ixgbe_pci_tbl[] = {
{PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_SGMII_L), board_x550em_a },
{PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_10G_T), board_x550em_a},
{PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_SFP), board_x550em_a },
{PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_1G_T), board_x550em_a_fw },
{PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_1G_T_L), board_x550em_a_fw },
/* required last entry */
{0, }
};
@ -180,6 +183,7 @@ MODULE_VERSION(DRV_VERSION);
static struct workqueue_struct *ixgbe_wq;
static bool ixgbe_check_cfg_remove(struct ixgbe_hw *hw, struct pci_dev *pdev);
static void ixgbe_watchdog_link_is_down(struct ixgbe_adapter *);
static int ixgbe_read_pci_cfg_word_parent(struct ixgbe_adapter *adapter,
u32 reg, u16 *value)
@ -2447,6 +2451,7 @@ static void ixgbe_check_overtemp_subtask(struct ixgbe_adapter *adapter)
{
struct ixgbe_hw *hw = &adapter->hw;
u32 eicr = adapter->interrupt_event;
s32 rc;
if (test_bit(__IXGBE_DOWN, &adapter->state))
return;
@ -2485,6 +2490,12 @@ static void ixgbe_check_overtemp_subtask(struct ixgbe_adapter *adapter)
return;
break;
case IXGBE_DEV_ID_X550EM_A_1G_T:
case IXGBE_DEV_ID_X550EM_A_1G_T_L:
rc = hw->phy.ops.check_overtemp(hw);
if (rc != IXGBE_ERR_OVERTEMP)
return;
break;
default:
if (adapter->hw.mac.type >= ixgbe_mac_X540)
return;
@ -2531,6 +2542,18 @@ static void ixgbe_check_overtemp_event(struct ixgbe_adapter *adapter, u32 eicr)
return;
}
return;
case ixgbe_mac_x550em_a:
if (eicr & IXGBE_EICR_GPI_SDP0_X550EM_a) {
adapter->interrupt_event = eicr;
adapter->flags2 |= IXGBE_FLAG2_TEMP_SENSOR_EVENT;
ixgbe_service_event_schedule(adapter);
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC,
IXGBE_EICR_GPI_SDP0_X550EM_a);
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICR,
IXGBE_EICR_GPI_SDP0_X550EM_a);
}
return;
case ixgbe_mac_X550:
case ixgbe_mac_X540:
if (!(eicr & IXGBE_EICR_TS))
return;
@ -5294,6 +5317,8 @@ void ixgbe_reinit_locked(struct ixgbe_adapter *adapter)
while (test_and_set_bit(__IXGBE_RESETTING, &adapter->state))
usleep_range(1000, 2000);
if (adapter->hw.phy.type == ixgbe_phy_fw)
ixgbe_watchdog_link_is_down(adapter);
ixgbe_down(adapter);
/*
* If SR-IOV enabled then wait a bit before bringing the adapter
@ -5553,6 +5578,31 @@ void ixgbe_down(struct ixgbe_adapter *adapter)
ixgbe_clean_all_rx_rings(adapter);
}
/**
* ixgbe_eee_capable - helper function to determine EEE support on X550
* @adapter: board private structure
*/
static void ixgbe_set_eee_capable(struct ixgbe_adapter *adapter)
{
struct ixgbe_hw *hw = &adapter->hw;
switch (hw->device_id) {
case IXGBE_DEV_ID_X550EM_A_1G_T:
case IXGBE_DEV_ID_X550EM_A_1G_T_L:
if (!hw->phy.eee_speeds_supported)
break;
adapter->flags2 |= IXGBE_FLAG2_EEE_CAPABLE;
if (!hw->phy.eee_speeds_advertised)
break;
adapter->flags2 |= IXGBE_FLAG2_EEE_ENABLED;
break;
default:
adapter->flags2 &= ~IXGBE_FLAG2_EEE_CAPABLE;
adapter->flags2 &= ~IXGBE_FLAG2_EEE_ENABLED;
break;
}
}
/**
* ixgbe_tx_timeout - Respond to a Tx Hang
* @netdev: network interface device structure
@ -5717,6 +5767,14 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter,
break;
case ixgbe_mac_x550em_a:
adapter->flags |= IXGBE_FLAG_GENEVE_OFFLOAD_CAPABLE;
switch (hw->device_id) {
case IXGBE_DEV_ID_X550EM_A_1G_T:
case IXGBE_DEV_ID_X550EM_A_1G_T_L:
adapter->flags2 |= IXGBE_FLAG2_TEMP_SENSOR_CAPABLE;
break;
default:
break;
}
/* fall through */
case ixgbe_mac_X550EM_x:
#ifdef CONFIG_IXGBE_DCB
@ -5730,6 +5788,8 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter,
#endif /* IXGBE_FCOE */
/* Fall Through */
case ixgbe_mac_X550:
if (hw->mac.type == ixgbe_mac_X550)
adapter->flags2 |= IXGBE_FLAG2_TEMP_SENSOR_CAPABLE;
#ifdef CONFIG_IXGBE_DCA
adapter->flags &= ~IXGBE_FLAG_DCA_CAPABLE;
#endif
@ -6807,6 +6867,9 @@ static void ixgbe_watchdog_link_is_up(struct ixgbe_adapter *adapter)
case IXGBE_LINK_SPEED_100_FULL:
speed_str = "100 Mbps";
break;
case IXGBE_LINK_SPEED_10_FULL:
speed_str = "10 Mbps";
break;
default:
speed_str = "unknown speed";
break;
@ -9595,6 +9658,7 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
hw->phy.reset_if_overtemp = true;
err = hw->mac.ops.reset_hw(hw);
hw->phy.reset_if_overtemp = false;
ixgbe_set_eee_capable(adapter);
if (err == IXGBE_ERR_SFP_NOT_PRESENT) {
err = 0;
} else if (err == IXGBE_ERR_SFP_NOT_SUPPORTED) {

View File

@ -784,6 +784,9 @@ s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw,
if (speed & IXGBE_LINK_SPEED_100_FULL)
hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_100_FULL;
if (speed & IXGBE_LINK_SPEED_10_FULL)
hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_10_FULL;
/* Setup link based on the new speed settings */
hw->phy.ops.setup_link(hw);

View File

@ -92,6 +92,8 @@
#define IXGBE_DEV_ID_X550EM_A_SGMII_L 0x15C7
#define IXGBE_DEV_ID_X550EM_A_10G_T 0x15C8
#define IXGBE_DEV_ID_X550EM_A_SFP 0x15CE
#define IXGBE_DEV_ID_X550EM_A_1G_T 0x15E4
#define IXGBE_DEV_ID_X550EM_A_1G_T_L 0x15E5
/* VF Device IDs */
#define IXGBE_DEV_ID_82599_VF 0x10ED
@ -1914,6 +1916,7 @@ enum {
#define IXGBE_LINKS_SPEED_10G_82599 0x30000000
#define IXGBE_LINKS_SPEED_1G_82599 0x20000000
#define IXGBE_LINKS_SPEED_100_82599 0x10000000
#define IXGBE_LINKS_SPEED_10_X550EM_A 0
#define IXGBE_LINK_UP_TIME 90 /* 9.0 Seconds */
#define IXGBE_AUTO_NEG_TIME 45 /* 4.5 Seconds */
@ -2926,6 +2929,7 @@ typedef u32 ixgbe_autoneg_advertised;
/* Link speed */
typedef u32 ixgbe_link_speed;
#define IXGBE_LINK_SPEED_UNKNOWN 0
#define IXGBE_LINK_SPEED_10_FULL 0x0002
#define IXGBE_LINK_SPEED_100_FULL 0x0008
#define IXGBE_LINK_SPEED_1GB_FULL 0x0020
#define IXGBE_LINK_SPEED_2_5GB_FULL 0x0400
@ -3141,6 +3145,7 @@ enum ixgbe_phy_type {
ixgbe_phy_qsfp_unknown,
ixgbe_phy_sfp_unsupported,
ixgbe_phy_sgmii,
ixgbe_phy_fw,
ixgbe_phy_generic
};
@ -3555,6 +3560,8 @@ struct ixgbe_phy_info {
bool reset_disable;
ixgbe_autoneg_advertised autoneg_advertised;
ixgbe_link_speed speeds_supported;
ixgbe_link_speed eee_speeds_supported;
ixgbe_link_speed eee_speeds_advertised;
enum ixgbe_smart_speed smart_speed;
bool smart_speed_active;
bool multispeed_fiber;

View File

@ -63,6 +63,18 @@ static s32 ixgbe_get_invariants_X550_a(struct ixgbe_hw *hw)
return 0;
}
static s32 ixgbe_get_invariants_X550_a_fw(struct ixgbe_hw *hw)
{
struct ixgbe_phy_info *phy = &hw->phy;
/* Start with X540 invariants, since so similar */
ixgbe_get_invariants_X540(hw);
phy->ops.set_phy_power = NULL;
return 0;
}
/** ixgbe_setup_mux_ctl - Setup ESDP register for I2C mux control
* @hw: pointer to hardware structure
**/
@ -447,6 +459,159 @@ s32 ixgbe_fw_phy_activity(struct ixgbe_hw *hw, u16 activity,
return IXGBE_ERR_HOST_INTERFACE_COMMAND;
}
static const struct {
u16 fw_speed;
ixgbe_link_speed phy_speed;
} ixgbe_fw_map[] = {
{ FW_PHY_ACT_LINK_SPEED_10, IXGBE_LINK_SPEED_10_FULL },
{ FW_PHY_ACT_LINK_SPEED_100, IXGBE_LINK_SPEED_100_FULL },
{ FW_PHY_ACT_LINK_SPEED_1G, IXGBE_LINK_SPEED_1GB_FULL },
{ FW_PHY_ACT_LINK_SPEED_2_5G, IXGBE_LINK_SPEED_2_5GB_FULL },
{ FW_PHY_ACT_LINK_SPEED_5G, IXGBE_LINK_SPEED_5GB_FULL },
{ FW_PHY_ACT_LINK_SPEED_10G, IXGBE_LINK_SPEED_10GB_FULL },
};
/**
* ixgbe_get_phy_id_fw - Get the phy ID via firmware command
* @hw: pointer to hardware structure
*
* Returns error code
*/
static s32 ixgbe_get_phy_id_fw(struct ixgbe_hw *hw)
{
u32 info[FW_PHY_ACT_DATA_COUNT] = { 0 };
u16 phy_speeds;
u16 phy_id_lo;
s32 rc;
u16 i;
if (hw->phy.id)
return 0;
rc = ixgbe_fw_phy_activity(hw, FW_PHY_ACT_GET_PHY_INFO, &info);
if (rc)
return rc;
hw->phy.speeds_supported = 0;
phy_speeds = info[0] & FW_PHY_INFO_SPEED_MASK;
for (i = 0; i < ARRAY_SIZE(ixgbe_fw_map); ++i) {
if (phy_speeds & ixgbe_fw_map[i].fw_speed)
hw->phy.speeds_supported |= ixgbe_fw_map[i].phy_speed;
}
hw->phy.id = info[0] & FW_PHY_INFO_ID_HI_MASK;
phy_id_lo = info[1] & FW_PHY_INFO_ID_LO_MASK;
hw->phy.id |= phy_id_lo & IXGBE_PHY_REVISION_MASK;
hw->phy.revision = phy_id_lo & ~IXGBE_PHY_REVISION_MASK;
if (!hw->phy.id || hw->phy.id == IXGBE_PHY_REVISION_MASK)
return IXGBE_ERR_PHY_ADDR_INVALID;
hw->phy.autoneg_advertised = hw->phy.speeds_supported;
hw->phy.eee_speeds_supported = IXGBE_LINK_SPEED_100_FULL |
IXGBE_LINK_SPEED_1GB_FULL;
hw->phy.eee_speeds_advertised = hw->phy.eee_speeds_supported;
return 0;
}
/**
* ixgbe_identify_phy_fw - Get PHY type based on firmware command
* @hw: pointer to hardware structure
*
* Returns error code
*/
static s32 ixgbe_identify_phy_fw(struct ixgbe_hw *hw)
{
if (hw->bus.lan_id)
hw->phy.phy_semaphore_mask = IXGBE_GSSR_PHY1_SM;
else
hw->phy.phy_semaphore_mask = IXGBE_GSSR_PHY0_SM;
hw->phy.type = ixgbe_phy_fw;
hw->phy.ops.read_reg = NULL;
hw->phy.ops.write_reg = NULL;
return ixgbe_get_phy_id_fw(hw);
}
/**
* ixgbe_shutdown_fw_phy - Shutdown a firmware-controlled PHY
* @hw: pointer to hardware structure
*
* Returns error code
*/
static s32 ixgbe_shutdown_fw_phy(struct ixgbe_hw *hw)
{
u32 setup[FW_PHY_ACT_DATA_COUNT] = { 0 };
setup[0] = FW_PHY_ACT_FORCE_LINK_DOWN_OFF;
return ixgbe_fw_phy_activity(hw, FW_PHY_ACT_FORCE_LINK_DOWN, &setup);
}
/**
* ixgbe_setup_fw_link - Setup firmware-controlled PHYs
* @hw: pointer to hardware structure
*/
static s32 ixgbe_setup_fw_link(struct ixgbe_hw *hw)
{
u32 setup[FW_PHY_ACT_DATA_COUNT] = { 0 };
s32 rc;
u16 i;
if (hw->phy.reset_disable || ixgbe_check_reset_blocked(hw))
return 0;
if (hw->fc.strict_ieee && hw->fc.requested_mode == ixgbe_fc_rx_pause) {
hw_err(hw, "rx_pause not valid in strict IEEE mode\n");
return IXGBE_ERR_INVALID_LINK_SETTINGS;
}
switch (hw->fc.requested_mode) {
case ixgbe_fc_full:
setup[0] |= FW_PHY_ACT_SETUP_LINK_PAUSE_RXTX <<
FW_PHY_ACT_SETUP_LINK_PAUSE_SHIFT;
break;
case ixgbe_fc_rx_pause:
setup[0] |= FW_PHY_ACT_SETUP_LINK_PAUSE_RX <<
FW_PHY_ACT_SETUP_LINK_PAUSE_SHIFT;
break;
case ixgbe_fc_tx_pause:
setup[0] |= FW_PHY_ACT_SETUP_LINK_PAUSE_TX <<
FW_PHY_ACT_SETUP_LINK_PAUSE_SHIFT;
break;
default:
break;
}
for (i = 0; i < ARRAY_SIZE(ixgbe_fw_map); ++i) {
if (hw->phy.autoneg_advertised & ixgbe_fw_map[i].phy_speed)
setup[0] |= ixgbe_fw_map[i].fw_speed;
}
setup[0] |= FW_PHY_ACT_SETUP_LINK_HP | FW_PHY_ACT_SETUP_LINK_AN;
if (hw->phy.eee_speeds_advertised)
setup[0] |= FW_PHY_ACT_SETUP_LINK_EEE;
rc = ixgbe_fw_phy_activity(hw, FW_PHY_ACT_SETUP_LINK, &setup);
if (rc)
return rc;
if (setup[0] == FW_PHY_ACT_SETUP_LINK_RSP_DOWN)
return IXGBE_ERR_OVERTEMP;
return 0;
}
/**
* ixgbe_fc_autoneg_fw - Set up flow control for FW-controlled PHYs
* @hw: pointer to hardware structure
*
* Called at init time to set up flow control.
*/
static s32 ixgbe_fc_autoneg_fw(struct ixgbe_hw *hw)
{
if (hw->fc.requested_mode == ixgbe_fc_default)
hw->fc.requested_mode = ixgbe_fc_full;
return ixgbe_setup_fw_link(hw);
}
/** ixgbe_init_eeprom_params_X550 - Initialize EEPROM params
* @hw: pointer to hardware structure
*
@ -1794,6 +1959,125 @@ ixgbe_setup_sgmii(struct ixgbe_hw *hw, __always_unused ixgbe_link_speed speed,
return rc;
}
/**
* ixgbe_setup_sgmii_fw - Set up link for sgmii with firmware-controlled PHYs
* @hw: pointer to hardware structure
*/
static s32 ixgbe_setup_sgmii_fw(struct ixgbe_hw *hw, ixgbe_link_speed speed,
bool autoneg_wait)
{
struct ixgbe_mac_info *mac = &hw->mac;
u32 lval, sval, flx_val;
s32 rc;
rc = mac->ops.read_iosf_sb_reg(hw,
IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, &lval);
if (rc)
return rc;
lval &= ~IXGBE_KRM_LINK_CTRL_1_TETH_AN_ENABLE;
lval &= ~IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_MASK;
lval |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_SGMII_EN;
lval |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_CLAUSE_37_EN;
lval &= ~IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_1G;
rc = mac->ops.write_iosf_sb_reg(hw,
IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, lval);
if (rc)
return rc;
rc = mac->ops.read_iosf_sb_reg(hw,
IXGBE_KRM_SGMII_CTRL(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, &sval);
if (rc)
return rc;
sval &= ~IXGBE_KRM_SGMII_CTRL_MAC_TAR_FORCE_10_D;
sval &= ~IXGBE_KRM_SGMII_CTRL_MAC_TAR_FORCE_100_D;
rc = mac->ops.write_iosf_sb_reg(hw,
IXGBE_KRM_SGMII_CTRL(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, sval);
if (rc)
return rc;
rc = mac->ops.write_iosf_sb_reg(hw,
IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, lval);
if (rc)
return rc;
rc = mac->ops.read_iosf_sb_reg(hw,
IXGBE_KRM_PMD_FLX_MASK_ST20(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, &flx_val);
if (rc)
return rc;
flx_val &= ~IXGBE_KRM_PMD_FLX_MASK_ST20_SPEED_MASK;
flx_val |= IXGBE_KRM_PMD_FLX_MASK_ST20_SPEED_AN;
flx_val &= ~IXGBE_KRM_PMD_FLX_MASK_ST20_AN_EN;
flx_val |= IXGBE_KRM_PMD_FLX_MASK_ST20_SGMII_EN;
flx_val |= IXGBE_KRM_PMD_FLX_MASK_ST20_AN37_EN;
rc = mac->ops.write_iosf_sb_reg(hw,
IXGBE_KRM_PMD_FLX_MASK_ST20(hw->bus.lan_id),
IXGBE_SB_IOSF_TARGET_KR_PHY, flx_val);
if (rc)
return rc;
ixgbe_restart_an_internal_phy_x550em(hw);
return hw->phy.ops.setup_link_speed(hw, speed, autoneg_wait);
}
/**
* ixgbe_fc_autoneg_sgmii_x550em_a - Enable flow control IEEE clause 37
* @hw: pointer to hardware structure
*
* Enable flow control according to IEEE clause 37.
*/
static void ixgbe_fc_autoneg_sgmii_x550em_a(struct ixgbe_hw *hw)
{
s32 status = IXGBE_ERR_FC_NOT_NEGOTIATED;
u32 info[FW_PHY_ACT_DATA_COUNT] = { 0 };
ixgbe_link_speed speed;
bool link_up;
/* AN should have completed when the cable was plugged in.
* Look for reasons to bail out. Bail out if:
* - FC autoneg is disabled, or if
* - link is not up.
*/
if (hw->fc.disable_fc_autoneg)
goto out;
hw->mac.ops.check_link(hw, &speed, &link_up, false);
if (!link_up)
goto out;
/* Check if auto-negotiation has completed */
status = ixgbe_fw_phy_activity(hw, FW_PHY_ACT_GET_LINK_INFO, &info);
if (status || !(info[0] & FW_PHY_ACT_GET_LINK_INFO_AN_COMPLETE)) {
status = IXGBE_ERR_FC_NOT_NEGOTIATED;
goto out;
}
/* Negotiate the flow control */
status = ixgbe_negotiate_fc(hw, info[0], info[0],
FW_PHY_ACT_GET_LINK_INFO_FC_RX,
FW_PHY_ACT_GET_LINK_INFO_FC_TX,
FW_PHY_ACT_GET_LINK_INFO_LP_FC_RX,
FW_PHY_ACT_GET_LINK_INFO_LP_FC_TX);
out:
if (!status) {
hw->fc.fc_was_autonegged = true;
} else {
hw->fc.fc_was_autonegged = false;
hw->fc.current_mode = hw->fc.requested_mode;
}
}
/** ixgbe_init_mac_link_ops_X550em_a - Init mac link function pointers
* @hw: pointer to hardware structure
**/
@ -1806,6 +2090,17 @@ static void ixgbe_init_mac_link_ops_X550em_a(struct ixgbe_hw *hw)
mac->ops.setup_fc = NULL;
mac->ops.fc_autoneg = ixgbe_fc_autoneg_fiber_x550em_a;
break;
case ixgbe_media_type_copper:
if (hw->device_id != IXGBE_DEV_ID_X550EM_A_1G_T &&
hw->device_id != IXGBE_DEV_ID_X550EM_A_1G_T_L) {
mac->ops.setup_link = ixgbe_setup_mac_link_t_X550em;
break;
}
mac->ops.fc_autoneg = ixgbe_fc_autoneg_sgmii_x550em_a;
mac->ops.setup_fc = ixgbe_fc_autoneg_fw;
mac->ops.setup_link = ixgbe_setup_sgmii_fw;
mac->ops.check_link = ixgbe_check_mac_link_generic;
break;
case ixgbe_media_type_backplane:
mac->ops.fc_autoneg = ixgbe_fc_autoneg_backplane_x550em_a;
mac->ops.setup_fc = ixgbe_setup_fc_backplane_x550em_a;
@ -1853,7 +2148,7 @@ static void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw)
mac->ops.setup_link = ixgbe_setup_mac_link_t_X550em;
mac->ops.setup_fc = ixgbe_setup_fc_generic;
mac->ops.check_link = ixgbe_check_link_t_X550em;
return;
break;
case ixgbe_media_type_backplane:
if (hw->device_id == IXGBE_DEV_ID_X550EM_A_SGMII ||
hw->device_id == IXGBE_DEV_ID_X550EM_A_SGMII_L)
@ -1896,6 +2191,12 @@ static s32 ixgbe_get_link_capabilities_X550em(struct ixgbe_hw *hw,
ixgbe_link_speed *speed,
bool *autoneg)
{
if (hw->phy.type == ixgbe_phy_fw) {
*autoneg = true;
*speed = hw->phy.speeds_supported;
return 0;
}
/* SFP */
if (hw->phy.media_type == ixgbe_media_type_fiber) {
/* CS4227 SFP must not enable auto-negotiation */
@ -2733,6 +3034,50 @@ static s32 ixgbe_enter_lplu_t_x550em(struct ixgbe_hw *hw)
return status;
}
/**
* ixgbe_reset_phy_fw - Reset firmware-controlled PHYs
* @hw: pointer to hardware structure
*/
static s32 ixgbe_reset_phy_fw(struct ixgbe_hw *hw)
{
u32 store[FW_PHY_ACT_DATA_COUNT] = { 0 };
s32 rc;
if (hw->phy.reset_disable || ixgbe_check_reset_blocked(hw))
return 0;
rc = ixgbe_fw_phy_activity(hw, FW_PHY_ACT_PHY_SW_RESET, &store);
if (rc)
return rc;
memset(store, 0, sizeof(store));
rc = ixgbe_fw_phy_activity(hw, FW_PHY_ACT_INIT_PHY, &store);
if (rc)
return rc;
return ixgbe_setup_fw_link(hw);
}
/**
* ixgbe_check_overtemp_fw - Check firmware-controlled PHYs for overtemp
* @hw: pointer to hardware structure
*/
static s32 ixgbe_check_overtemp_fw(struct ixgbe_hw *hw)
{
u32 store[FW_PHY_ACT_DATA_COUNT] = { 0 };
s32 rc;
rc = ixgbe_fw_phy_activity(hw, FW_PHY_ACT_GET_LINK_INFO, &store);
if (rc)
return rc;
if (store[0] & FW_PHY_ACT_GET_LINK_INFO_TEMP) {
ixgbe_shutdown_fw_phy(hw);
return IXGBE_ERR_OVERTEMP;
}
return 0;
}
/**
* ixgbe_read_mng_if_sel_x550em - Read NW_MNG_IF_SEL register
* @hw: pointer to hardware structure
@ -2819,6 +3164,10 @@ static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw)
phy->ops.handle_lasi = ixgbe_handle_lasi_ext_t_x550em;
phy->ops.reset = ixgbe_reset_phy_t_X550em;
break;
case ixgbe_phy_fw:
phy->ops.setup_link = ixgbe_setup_fw_link;
phy->ops.reset = ixgbe_reset_phy_fw;
break;
default:
break;
}
@ -2856,6 +3205,8 @@ static enum ixgbe_media_type ixgbe_get_media_type_X550em(struct ixgbe_hw *hw)
case IXGBE_DEV_ID_X550EM_X_1G_T:
case IXGBE_DEV_ID_X550EM_X_10G_T:
case IXGBE_DEV_ID_X550EM_A_10G_T:
case IXGBE_DEV_ID_X550EM_A_1G_T:
case IXGBE_DEV_ID_X550EM_A_1G_T_L:
media_type = ixgbe_media_type_copper;
break;
default:
@ -2923,6 +3274,13 @@ static void ixgbe_set_mdio_speed(struct ixgbe_hw *hw)
hlreg0 &= ~IXGBE_HLREG0_MDCSPD;
IXGBE_WRITE_REG(hw, IXGBE_HLREG0, hlreg0);
break;
case IXGBE_DEV_ID_X550EM_A_1G_T:
case IXGBE_DEV_ID_X550EM_A_1G_T_L:
/* Select fast MDIO clock speed for these devices */
hlreg0 = IXGBE_READ_REG(hw, IXGBE_HLREG0);
hlreg0 |= IXGBE_HLREG0_MDCSPD;
IXGBE_WRITE_REG(hw, IXGBE_HLREG0, hlreg0);
break;
default:
break;
}
@ -3434,6 +3792,27 @@ static struct ixgbe_mac_operations mac_ops_x550em_a = {
.write_iosf_sb_reg = ixgbe_write_iosf_sb_reg_x550a,
};
static struct ixgbe_mac_operations mac_ops_x550em_a_fw = {
X550_COMMON_MAC
.led_on = ixgbe_led_on_generic,
.led_off = ixgbe_led_off_generic,
.init_led_link_act = ixgbe_init_led_link_act_generic,
.reset_hw = ixgbe_reset_hw_X550em,
.get_media_type = ixgbe_get_media_type_X550em,
.get_san_mac_addr = NULL,
.get_wwn_prefix = NULL,
.setup_link = NULL, /* defined later */
.get_link_capabilities = ixgbe_get_link_capabilities_X550em,
.get_bus_info = ixgbe_get_bus_info_X550em,
.setup_sfp = ixgbe_setup_sfp_modules_X550em,
.acquire_swfw_sync = ixgbe_acquire_swfw_sync_x550em_a,
.release_swfw_sync = ixgbe_release_swfw_sync_x550em_a,
.setup_fc = ixgbe_setup_fc_x550em,
.fc_autoneg = ixgbe_fc_autoneg,
.read_iosf_sb_reg = ixgbe_read_iosf_sb_reg_x550a,
.write_iosf_sb_reg = ixgbe_write_iosf_sb_reg_x550a,
};
#define X550_COMMON_EEP \
.read = &ixgbe_read_ee_hostif_X550, \
.read_buffer = &ixgbe_read_ee_hostif_buffer_X550, \
@ -3463,11 +3842,11 @@ static const struct ixgbe_eeprom_operations eeprom_ops_X550EM_x = {
.read_i2c_eeprom = &ixgbe_read_i2c_eeprom_generic, \
.write_i2c_eeprom = &ixgbe_write_i2c_eeprom_generic, \
.setup_link = &ixgbe_setup_phy_link_generic, \
.set_phy_power = NULL, \
.check_overtemp = &ixgbe_tn_check_overtemp,
.set_phy_power = NULL,
static const struct ixgbe_phy_operations phy_ops_X550 = {
X550_COMMON_PHY
.check_overtemp = &ixgbe_tn_check_overtemp,
.init = NULL,
.identify = &ixgbe_identify_phy_generic,
.read_reg = &ixgbe_read_phy_reg_generic,
@ -3476,6 +3855,7 @@ static const struct ixgbe_phy_operations phy_ops_X550 = {
static const struct ixgbe_phy_operations phy_ops_X550EM_x = {
X550_COMMON_PHY
.check_overtemp = &ixgbe_tn_check_overtemp,
.init = &ixgbe_init_phy_ops_X550em,
.identify = &ixgbe_identify_phy_x550em,
.read_reg = &ixgbe_read_phy_reg_generic,
@ -3484,6 +3864,7 @@ static const struct ixgbe_phy_operations phy_ops_X550EM_x = {
static const struct ixgbe_phy_operations phy_ops_x550em_a = {
X550_COMMON_PHY
.check_overtemp = &ixgbe_tn_check_overtemp,
.init = &ixgbe_init_phy_ops_X550em,
.identify = &ixgbe_identify_phy_x550em,
.read_reg = &ixgbe_read_phy_reg_x550a,
@ -3492,6 +3873,17 @@ static const struct ixgbe_phy_operations phy_ops_x550em_a = {
.write_reg_mdi = &ixgbe_write_phy_reg_mdi,
};
static const struct ixgbe_phy_operations phy_ops_x550em_a_fw = {
X550_COMMON_PHY
.check_overtemp = ixgbe_check_overtemp_fw,
.init = ixgbe_init_phy_ops_X550em,
.identify = ixgbe_identify_phy_fw,
.read_reg = NULL,
.write_reg = NULL,
.read_reg_mdi = NULL,
.write_reg_mdi = NULL,
};
static const struct ixgbe_link_operations link_ops_x550em_x = {
.read_link = &ixgbe_read_i2c_combined_generic,
.read_link_unlocked = &ixgbe_read_i2c_combined_generic_unlocked,
@ -3541,3 +3933,13 @@ const struct ixgbe_info ixgbe_x550em_a_info = {
.mbx_ops = &mbx_ops_generic,
.mvals = ixgbe_mvals_x550em_a,
};
const struct ixgbe_info ixgbe_x550em_a_fw_info = {
.mac = ixgbe_mac_x550em_a,
.get_invariants = ixgbe_get_invariants_X550_a_fw,
.mac_ops = &mac_ops_x550em_a_fw,
.eeprom_ops = &eeprom_ops_X550EM_x,
.phy_ops = &phy_ops_x550em_a_fw,
.mbx_ops = &mbx_ops_generic,
.mvals = ixgbe_mvals_x550em_a,
};