Commit 57128197 authored by Jeff Kirsher's avatar Jeff Kirsher Committed by Jeff Garzik

[PATCH] e1000: Fix SoL/IDER link and loopback

Fix so that if a SoL/IDER session is active, do not allow operations which require a PHY reset and instead log a message.
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: default avatarJesse Brandeburg <jesse.brandeburg@intel.com>
Signed-off-by: default avatarJohn Ronciak <john.ronciak@intel.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@pobox.com>
parent 545c67c0
...@@ -183,7 +183,15 @@ e1000_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) ...@@ -183,7 +183,15 @@ e1000_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw; struct e1000_hw *hw = &adapter->hw;
if(ecmd->autoneg == AUTONEG_ENABLE) { /* When SoL/IDER sessions are active, autoneg/speed/duplex
* cannot be changed */
if (e1000_check_phy_reset_block(hw)) {
DPRINTK(DRV, ERR, "Cannot change link characteristics "
"when SoL/IDER is active.\n");
return -EINVAL;
}
if (ecmd->autoneg == AUTONEG_ENABLE) {
hw->autoneg = 1; hw->autoneg = 1;
if(hw->media_type == e1000_media_type_fiber) if(hw->media_type == e1000_media_type_fiber)
hw->autoneg_advertised = ADVERTISED_1000baseT_Full | hw->autoneg_advertised = ADVERTISED_1000baseT_Full |
...@@ -562,29 +570,10 @@ e1000_get_drvinfo(struct net_device *netdev, ...@@ -562,29 +570,10 @@ e1000_get_drvinfo(struct net_device *netdev,
struct ethtool_drvinfo *drvinfo) struct ethtool_drvinfo *drvinfo)
{ {
struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_adapter *adapter = netdev_priv(netdev);
char firmware_version[32];
uint16_t eeprom_data;
strncpy(drvinfo->driver, e1000_driver_name, 32); strncpy(drvinfo->driver, e1000_driver_name, 32);
strncpy(drvinfo->version, e1000_driver_version, 32); strncpy(drvinfo->version, e1000_driver_version, 32);
strncpy(drvinfo->fw_version, "N/A", 32);
/* EEPROM image version # is reported as firware version # for
* 8257{1|2|3} controllers */
e1000_read_eeprom(&adapter->hw, 5, 1, &eeprom_data);
switch (adapter->hw.mac_type) {
case e1000_82571:
case e1000_82572:
case e1000_82573:
sprintf(firmware_version, "%d.%d-%d",
(eeprom_data & 0xF000) >> 12,
(eeprom_data & 0x0FF0) >> 4,
eeprom_data & 0x000F);
break;
default:
sprintf(firmware_version, "n/a");
}
strncpy(drvinfo->fw_version, firmware_version, 32);
strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32);
drvinfo->n_stats = E1000_STATS_LEN; drvinfo->n_stats = E1000_STATS_LEN;
drvinfo->testinfo_len = E1000_TEST_LEN; drvinfo->testinfo_len = E1000_TEST_LEN;
...@@ -990,10 +979,8 @@ e1000_free_desc_rings(struct e1000_adapter *adapter) ...@@ -990,10 +979,8 @@ e1000_free_desc_rings(struct e1000_adapter *adapter)
kfree(txdr->buffer_info); kfree(txdr->buffer_info);
txdr->buffer_info = NULL; txdr->buffer_info = NULL;
kfree(rxdr->buffer_info); kfree(rxdr->buffer_info);
rxdr->buffer_info = NULL; rxdr->buffer_info = NULL;
return; return;
} }
...@@ -1328,32 +1315,21 @@ static int ...@@ -1328,32 +1315,21 @@ static int
e1000_setup_loopback_test(struct e1000_adapter *adapter) e1000_setup_loopback_test(struct e1000_adapter *adapter)
{ {
uint32_t rctl; uint32_t rctl;
struct e1000_hw *hw = &adapter->hw;
if (hw->media_type == e1000_media_type_fiber || if(adapter->hw.media_type == e1000_media_type_fiber ||
hw->media_type == e1000_media_type_internal_serdes) { adapter->hw.media_type == e1000_media_type_internal_serdes) {
switch (hw->mac_type) { if(adapter->hw.mac_type == e1000_82545 ||
case e1000_82545: adapter->hw.mac_type == e1000_82546 ||
case e1000_82546: adapter->hw.mac_type == e1000_82545_rev_3 ||
case e1000_82545_rev_3: adapter->hw.mac_type == e1000_82546_rev_3)
case e1000_82546_rev_3:
return e1000_set_phy_loopback(adapter); return e1000_set_phy_loopback(adapter);
break; else {
case e1000_82571: rctl = E1000_READ_REG(&adapter->hw, RCTL);
case e1000_82572:
#define E1000_SERDES_LB_ON 0x410
e1000_set_phy_loopback(adapter);
E1000_WRITE_REG(hw, SCTL, E1000_SERDES_LB_ON);
msec_delay(10);
return 0;
break;
default:
rctl = E1000_READ_REG(hw, RCTL);
rctl |= E1000_RCTL_LBM_TCVR; rctl |= E1000_RCTL_LBM_TCVR;
E1000_WRITE_REG(hw, RCTL, rctl); E1000_WRITE_REG(&adapter->hw, RCTL, rctl);
return 0; return 0;
} }
} else if (hw->media_type == e1000_media_type_copper) } else if(adapter->hw.media_type == e1000_media_type_copper)
return e1000_set_phy_loopback(adapter); return e1000_set_phy_loopback(adapter);
return 7; return 7;
...@@ -1364,36 +1340,25 @@ e1000_loopback_cleanup(struct e1000_adapter *adapter) ...@@ -1364,36 +1340,25 @@ e1000_loopback_cleanup(struct e1000_adapter *adapter)
{ {
uint32_t rctl; uint32_t rctl;
uint16_t phy_reg; uint16_t phy_reg;
struct e1000_hw *hw = &adapter->hw;
rctl = E1000_READ_REG(&adapter->hw, RCTL); rctl = E1000_READ_REG(&adapter->hw, RCTL);
rctl &= ~(E1000_RCTL_LBM_TCVR | E1000_RCTL_LBM_MAC); rctl &= ~(E1000_RCTL_LBM_TCVR | E1000_RCTL_LBM_MAC);
E1000_WRITE_REG(&adapter->hw, RCTL, rctl); E1000_WRITE_REG(&adapter->hw, RCTL, rctl);
switch (hw->mac_type) { if(adapter->hw.media_type == e1000_media_type_copper ||
case e1000_82571: ((adapter->hw.media_type == e1000_media_type_fiber ||
case e1000_82572: adapter->hw.media_type == e1000_media_type_internal_serdes) &&
if (hw->media_type == e1000_media_type_fiber || (adapter->hw.mac_type == e1000_82545 ||
hw->media_type == e1000_media_type_internal_serdes){ adapter->hw.mac_type == e1000_82546 ||
#define E1000_SERDES_LB_OFF 0x400 adapter->hw.mac_type == e1000_82545_rev_3 ||
E1000_WRITE_REG(hw, SCTL, E1000_SERDES_LB_OFF); adapter->hw.mac_type == e1000_82546_rev_3))) {
msec_delay(10); adapter->hw.autoneg = TRUE;
break; e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &phy_reg);
} if(phy_reg & MII_CR_LOOPBACK) {
/* fall thru for Cu adapters */
case e1000_82545:
case e1000_82546:
case e1000_82545_rev_3:
case e1000_82546_rev_3:
default:
hw->autoneg = TRUE;
e1000_read_phy_reg(hw, PHY_CTRL, &phy_reg);
if (phy_reg & MII_CR_LOOPBACK) {
phy_reg &= ~MII_CR_LOOPBACK; phy_reg &= ~MII_CR_LOOPBACK;
e1000_write_phy_reg(hw, PHY_CTRL, phy_reg); e1000_write_phy_reg(&adapter->hw, PHY_CTRL, phy_reg);
e1000_phy_reset(hw); e1000_phy_reset(&adapter->hw);
} }
break;
} }
} }
...@@ -1488,14 +1453,25 @@ e1000_run_loopback_test(struct e1000_adapter *adapter) ...@@ -1488,14 +1453,25 @@ e1000_run_loopback_test(struct e1000_adapter *adapter)
static int static int
e1000_loopback_test(struct e1000_adapter *adapter, uint64_t *data) e1000_loopback_test(struct e1000_adapter *adapter, uint64_t *data)
{ {
if((*data = e1000_setup_desc_rings(adapter))) goto err_loopback; /* PHY loopback cannot be performed if SoL/IDER
if((*data = e1000_setup_loopback_test(adapter))) * sessions are active */
goto err_loopback_setup; if (e1000_check_phy_reset_block(&adapter->hw)) {
DPRINTK(DRV, ERR, "Cannot do PHY loopback test "
"when SoL/IDER is active.\n");
*data = 0;
goto out;
}
if ((*data = e1000_setup_desc_rings(adapter)))
goto out;
if ((*data = e1000_setup_loopback_test(adapter)))
goto err_loopback;
*data = e1000_run_loopback_test(adapter); *data = e1000_run_loopback_test(adapter);
e1000_loopback_cleanup(adapter); e1000_loopback_cleanup(adapter);
err_loopback_setup:
e1000_free_desc_rings(adapter);
err_loopback: err_loopback:
e1000_free_desc_rings(adapter);
out:
return *data; return *data;
} }
...@@ -1722,14 +1698,6 @@ e1000_phys_id(struct net_device *netdev, uint32_t data) ...@@ -1722,14 +1698,6 @@ e1000_phys_id(struct net_device *netdev, uint32_t data)
msleep_interruptible(data * 1000); msleep_interruptible(data * 1000);
del_timer_sync(&adapter->blink_timer); del_timer_sync(&adapter->blink_timer);
} }
else if(adapter->hw.mac_type < e1000_82573) {
E1000_WRITE_REG(&adapter->hw, LEDCTL, (E1000_LEDCTL_LED2_BLINK_RATE |
E1000_LEDCTL_LED0_BLINK | E1000_LEDCTL_LED2_BLINK |
(E1000_LEDCTL_MODE_LED_ON << E1000_LEDCTL_LED2_MODE_SHIFT) |
(E1000_LEDCTL_MODE_LINK_ACTIVITY << E1000_LEDCTL_LED0_MODE_SHIFT) |
(E1000_LEDCTL_MODE_LED_OFF << E1000_LEDCTL_LED1_MODE_SHIFT)));
msleep_interruptible(data * 1000);
}
else { else {
E1000_WRITE_REG(&adapter->hw, LEDCTL, (E1000_LEDCTL_LED2_BLINK_RATE | E1000_WRITE_REG(&adapter->hw, LEDCTL, (E1000_LEDCTL_LED2_BLINK_RATE |
E1000_LEDCTL_LED1_BLINK | E1000_LEDCTL_LED2_BLINK | E1000_LEDCTL_LED1_BLINK | E1000_LEDCTL_LED2_BLINK |
......
...@@ -378,6 +378,8 @@ void ...@@ -378,6 +378,8 @@ void
e1000_down(struct e1000_adapter *adapter) e1000_down(struct e1000_adapter *adapter)
{ {
struct net_device *netdev = adapter->netdev; struct net_device *netdev = adapter->netdev;
boolean_t mng_mode_enabled = (adapter->hw.mac_type >= e1000_82571) &&
e1000_check_mng_mode(&adapter->hw);
e1000_irq_disable(adapter); e1000_irq_disable(adapter);
#ifdef CONFIG_E1000_MQ #ifdef CONFIG_E1000_MQ
...@@ -405,12 +407,16 @@ e1000_down(struct e1000_adapter *adapter) ...@@ -405,12 +407,16 @@ e1000_down(struct e1000_adapter *adapter)
e1000_clean_all_tx_rings(adapter); e1000_clean_all_tx_rings(adapter);
e1000_clean_all_rx_rings(adapter); e1000_clean_all_rx_rings(adapter);
/* If WoL is not enabled and management mode is not IAMT /* Power down the PHY so no link is implied when interface is down *
* Power down the PHY so no link is implied when interface is down */ * The PHY cannot be powered down if any of the following is TRUE *
if(!adapter->wol && adapter->hw.mac_type >= e1000_82540 && * (a) WoL is enabled
* (b) AMT is active
* (c) SoL/IDER session is active */
if (!adapter->wol && adapter->hw.mac_type >= e1000_82540 &&
adapter->hw.media_type == e1000_media_type_copper && adapter->hw.media_type == e1000_media_type_copper &&
!e1000_check_mng_mode(&adapter->hw) && !(E1000_READ_REG(&adapter->hw, MANC) & E1000_MANC_SMBUS_EN) &&
!(E1000_READ_REG(&adapter->hw, MANC) & E1000_MANC_SMBUS_EN)) { !mng_mode_enabled &&
!e1000_check_phy_reset_block(&adapter->hw)) {
uint16_t mii_reg; uint16_t mii_reg;
e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &mii_reg); e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &mii_reg);
mii_reg |= MII_CR_POWER_DOWN; mii_reg |= MII_CR_POWER_DOWN;
......
...@@ -584,6 +584,12 @@ e1000_check_copper_options(struct e1000_adapter *adapter) ...@@ -584,6 +584,12 @@ e1000_check_copper_options(struct e1000_adapter *adapter)
.p = dplx_list }} .p = dplx_list }}
}; };
if (e1000_check_phy_reset_block(&adapter->hw)) {
DPRINTK(PROBE, INFO,
"Link active due to SoL/IDER Session. "
"Speed/Duplex/AutoNeg parameter ignored.\n");
return;
}
if (num_Duplex > bd) { if (num_Duplex > bd) {
dplx = Duplex[bd]; dplx = Duplex[bd];
e1000_validate_option(&dplx, &opt, adapter); e1000_validate_option(&dplx, &opt, adapter);
......
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