[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: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
Signed-off-by: John Ronciak <john.ronciak@intel.com>
Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c
index c88f1a3..c929277 100644
--- a/drivers/net/e1000/e1000_ethtool.c
+++ b/drivers/net/e1000/e1000_ethtool.c
@@ -183,7 +183,15 @@
struct e1000_adapter *adapter = netdev_priv(netdev);
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;
if(hw->media_type == e1000_media_type_fiber)
hw->autoneg_advertised = ADVERTISED_1000baseT_Full |
@@ -562,29 +570,10 @@
struct ethtool_drvinfo *drvinfo)
{
struct e1000_adapter *adapter = netdev_priv(netdev);
- char firmware_version[32];
- uint16_t eeprom_data;
strncpy(drvinfo->driver, e1000_driver_name, 32);
strncpy(drvinfo->version, e1000_driver_version, 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->fw_version, "N/A", 32);
strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32);
drvinfo->n_stats = E1000_STATS_LEN;
drvinfo->testinfo_len = E1000_TEST_LEN;
@@ -990,10 +979,8 @@
kfree(txdr->buffer_info);
txdr->buffer_info = NULL;
-
kfree(rxdr->buffer_info);
rxdr->buffer_info = NULL;
-
return;
}
@@ -1328,32 +1315,21 @@
e1000_setup_loopback_test(struct e1000_adapter *adapter)
{
uint32_t rctl;
- struct e1000_hw *hw = &adapter->hw;
- if (hw->media_type == e1000_media_type_fiber ||
- hw->media_type == e1000_media_type_internal_serdes) {
- switch (hw->mac_type) {
- case e1000_82545:
- case e1000_82546:
- case e1000_82545_rev_3:
- case e1000_82546_rev_3:
+ if(adapter->hw.media_type == e1000_media_type_fiber ||
+ adapter->hw.media_type == e1000_media_type_internal_serdes) {
+ if(adapter->hw.mac_type == e1000_82545 ||
+ adapter->hw.mac_type == e1000_82546 ||
+ adapter->hw.mac_type == e1000_82545_rev_3 ||
+ adapter->hw.mac_type == e1000_82546_rev_3)
return e1000_set_phy_loopback(adapter);
- break;
- case e1000_82571:
- 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);
+ else {
+ rctl = E1000_READ_REG(&adapter->hw, RCTL);
rctl |= E1000_RCTL_LBM_TCVR;
- E1000_WRITE_REG(hw, RCTL, rctl);
+ E1000_WRITE_REG(&adapter->hw, RCTL, rctl);
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 7;
@@ -1364,36 +1340,25 @@
{
uint32_t rctl;
uint16_t phy_reg;
- struct e1000_hw *hw = &adapter->hw;
rctl = E1000_READ_REG(&adapter->hw, RCTL);
rctl &= ~(E1000_RCTL_LBM_TCVR | E1000_RCTL_LBM_MAC);
E1000_WRITE_REG(&adapter->hw, RCTL, rctl);
- switch (hw->mac_type) {
- case e1000_82571:
- case e1000_82572:
- if (hw->media_type == e1000_media_type_fiber ||
- hw->media_type == e1000_media_type_internal_serdes){
-#define E1000_SERDES_LB_OFF 0x400
- E1000_WRITE_REG(hw, SCTL, E1000_SERDES_LB_OFF);
- msec_delay(10);
- break;
- }
- /* 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) {
+ if(adapter->hw.media_type == e1000_media_type_copper ||
+ ((adapter->hw.media_type == e1000_media_type_fiber ||
+ adapter->hw.media_type == e1000_media_type_internal_serdes) &&
+ (adapter->hw.mac_type == e1000_82545 ||
+ adapter->hw.mac_type == e1000_82546 ||
+ adapter->hw.mac_type == e1000_82545_rev_3 ||
+ adapter->hw.mac_type == e1000_82546_rev_3))) {
+ adapter->hw.autoneg = TRUE;
+ e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &phy_reg);
+ if(phy_reg & MII_CR_LOOPBACK) {
phy_reg &= ~MII_CR_LOOPBACK;
- e1000_write_phy_reg(hw, PHY_CTRL, phy_reg);
- e1000_phy_reset(hw);
+ e1000_write_phy_reg(&adapter->hw, PHY_CTRL, phy_reg);
+ e1000_phy_reset(&adapter->hw);
}
- break;
}
}
@@ -1488,14 +1453,25 @@
static int
e1000_loopback_test(struct e1000_adapter *adapter, uint64_t *data)
{
- if((*data = e1000_setup_desc_rings(adapter))) goto err_loopback;
- if((*data = e1000_setup_loopback_test(adapter)))
- goto err_loopback_setup;
+ /* PHY loopback cannot be performed if SoL/IDER
+ * sessions are active */
+ 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);
e1000_loopback_cleanup(adapter);
-err_loopback_setup:
- e1000_free_desc_rings(adapter);
+
err_loopback:
+ e1000_free_desc_rings(adapter);
+out:
return *data;
}
@@ -1722,14 +1698,6 @@
msleep_interruptible(data * 1000);
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 {
E1000_WRITE_REG(&adapter->hw, LEDCTL, (E1000_LEDCTL_LED2_BLINK_RATE |
E1000_LEDCTL_LED1_BLINK | E1000_LEDCTL_LED2_BLINK |
diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c
index 22c8286..66cf174 100644
--- a/drivers/net/e1000/e1000_main.c
+++ b/drivers/net/e1000/e1000_main.c
@@ -378,6 +378,8 @@
e1000_down(struct e1000_adapter *adapter)
{
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);
#ifdef CONFIG_E1000_MQ
@@ -405,12 +407,16 @@
e1000_clean_all_tx_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 */
- if(!adapter->wol && adapter->hw.mac_type >= e1000_82540 &&
+ /* 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 *
+ * (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 &&
- !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;
e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &mii_reg);
mii_reg |= MII_CR_POWER_DOWN;
diff --git a/drivers/net/e1000/e1000_param.c b/drivers/net/e1000/e1000_param.c
index ccbbe5a..852841f 100644
--- a/drivers/net/e1000/e1000_param.c
+++ b/drivers/net/e1000/e1000_param.c
@@ -584,6 +584,12 @@
.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) {
dplx = Duplex[bd];
e1000_validate_option(&dplx, &opt, adapter);