diff options
Diffstat (limited to 'drivers/net/ethernet/intel/igc/igc_ethtool.c')
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_ethtool.c | 277 |
1 files changed, 194 insertions, 83 deletions
diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c index 0122009bedd0..89fe2788a565 100644 --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c @@ -1460,6 +1460,26 @@ static int igc_ethtool_set_rxnfc(struct net_device *dev, } } +/** + * igc_write_rss_key - Program the RSS key into device registers + * @adapter: board private structure + * + * Write the RSS key stored in adapter->rss_key to the IGC_RSSRK registers. + * Each 32-bit chunk of the key is read using get_unaligned_le32() and written + * to the appropriate register. + */ +void igc_write_rss_key(struct igc_adapter *adapter) +{ + struct igc_hw *hw = &adapter->hw; + u32 val; + int i; + + for (i = 0; i < IGC_RSS_KEY_SIZE / 4; i++) { + val = get_unaligned_le32(&adapter->rss_key[i * 4]); + wr32(IGC_RSSRK(i), val); + } +} + void igc_write_rss_indir_tbl(struct igc_adapter *adapter) { struct igc_hw *hw = &adapter->hw; @@ -1482,6 +1502,11 @@ void igc_write_rss_indir_tbl(struct igc_adapter *adapter) } } +static u32 igc_ethtool_get_rxfh_key_size(struct net_device *netdev) +{ + return IGC_RSS_KEY_SIZE; +} + static u32 igc_ethtool_get_rxfh_indir_size(struct net_device *netdev) { return IGC_RETA_SIZE; @@ -1494,10 +1519,13 @@ static int igc_ethtool_get_rxfh(struct net_device *netdev, int i; rxfh->hfunc = ETH_RSS_HASH_TOP; - if (!rxfh->indir) - return 0; - for (i = 0; i < IGC_RETA_SIZE; i++) - rxfh->indir[i] = adapter->rss_indir_tbl[i]; + + if (rxfh->indir) + for (i = 0; i < IGC_RETA_SIZE; i++) + rxfh->indir[i] = adapter->rss_indir_tbl[i]; + + if (rxfh->key) + memcpy(rxfh->key, adapter->rss_key, sizeof(adapter->rss_key)); return 0; } @@ -1511,24 +1539,28 @@ static int igc_ethtool_set_rxfh(struct net_device *netdev, int i; /* We do not allow change in unsupported parameters */ - if (rxfh->key || - (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE && - rxfh->hfunc != ETH_RSS_HASH_TOP)) + if (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE && + rxfh->hfunc != ETH_RSS_HASH_TOP) return -EOPNOTSUPP; - if (!rxfh->indir) - return 0; - num_queues = adapter->rss_queues; + if (rxfh->indir) { + num_queues = adapter->rss_queues; - /* Verify user input. */ - for (i = 0; i < IGC_RETA_SIZE; i++) - if (rxfh->indir[i] >= num_queues) - return -EINVAL; + /* Verify user input. */ + for (i = 0; i < IGC_RETA_SIZE; i++) + if (rxfh->indir[i] >= num_queues) + return -EINVAL; - for (i = 0; i < IGC_RETA_SIZE; i++) - adapter->rss_indir_tbl[i] = rxfh->indir[i]; + for (i = 0; i < IGC_RETA_SIZE; i++) + adapter->rss_indir_tbl[i] = rxfh->indir[i]; + + igc_write_rss_indir_tbl(adapter); + } - igc_write_rss_indir_tbl(adapter); + if (rxfh->key) { + memcpy(adapter->rss_key, rxfh->key, sizeof(adapter->rss_key)); + igc_write_rss_key(adapter); + } return 0; } @@ -1914,44 +1946,58 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev, ethtool_link_ksettings_add_link_mode(cmd, supported, TP); ethtool_link_ksettings_add_link_mode(cmd, advertising, TP); - /* advertising link modes */ - if (hw->phy.autoneg_advertised & ADVERTISE_10_HALF) - ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Half); - if (hw->phy.autoneg_advertised & ADVERTISE_10_FULL) - ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Full); - if (hw->phy.autoneg_advertised & ADVERTISE_100_HALF) - ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Half); - if (hw->phy.autoneg_advertised & ADVERTISE_100_FULL) - ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Full); - if (hw->phy.autoneg_advertised & ADVERTISE_1000_FULL) - ethtool_link_ksettings_add_link_mode(cmd, advertising, 1000baseT_Full); - if (hw->phy.autoneg_advertised & ADVERTISE_2500_FULL) - ethtool_link_ksettings_add_link_mode(cmd, advertising, 2500baseT_Full); - /* set autoneg settings */ ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg); - ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg); + if (hw->mac.autoneg_enabled) { + ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg); + cmd->base.autoneg = AUTONEG_ENABLE; + + /* advertising link modes only apply when autoneg is on */ + if (hw->phy.autoneg_advertised & ADVERTISE_10_HALF) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 10baseT_Half); + if (hw->phy.autoneg_advertised & ADVERTISE_10_FULL) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 10baseT_Full); + if (hw->phy.autoneg_advertised & ADVERTISE_100_HALF) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 100baseT_Half); + if (hw->phy.autoneg_advertised & ADVERTISE_100_FULL) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 100baseT_Full); + if (hw->phy.autoneg_advertised & ADVERTISE_1000_FULL) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 1000baseT_Full); + if (hw->phy.autoneg_advertised & ADVERTISE_2500_FULL) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 2500baseT_Full); + + /* Set pause flow control advertising */ + switch (hw->fc.requested_mode) { + case igc_fc_full: + ethtool_link_ksettings_add_link_mode(cmd, advertising, + Pause); + break; + case igc_fc_rx_pause: + ethtool_link_ksettings_add_link_mode(cmd, advertising, + Pause); + ethtool_link_ksettings_add_link_mode(cmd, advertising, + Asym_Pause); + break; + case igc_fc_tx_pause: + ethtool_link_ksettings_add_link_mode(cmd, advertising, + Asym_Pause); + break; + default: + break; + } + } else { + cmd->base.autoneg = AUTONEG_DISABLE; + } - /* Set pause flow control settings */ + /* Pause is always supported */ ethtool_link_ksettings_add_link_mode(cmd, supported, Pause); - switch (hw->fc.requested_mode) { - case igc_fc_full: - ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause); - break; - case igc_fc_rx_pause: - ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause); - ethtool_link_ksettings_add_link_mode(cmd, advertising, - Asym_Pause); - break; - case igc_fc_tx_pause: - ethtool_link_ksettings_add_link_mode(cmd, advertising, - Asym_Pause); - break; - default: - break; - } - status = pm_runtime_suspended(&adapter->pdev->dev) ? 0 : rd32(IGC_STATUS); @@ -1983,7 +2029,6 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev, cmd->base.duplex = DUPLEX_UNKNOWN; } cmd->base.speed = speed; - cmd->base.autoneg = AUTONEG_ENABLE; /* MDI-X => 2; MDI =>1; Invalid =>0 */ if (hw->phy.media_type == igc_media_type_copper) @@ -2000,37 +2045,50 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev, return 0; } -static int -igc_ethtool_set_link_ksettings(struct net_device *netdev, - const struct ethtool_link_ksettings *cmd) +/** + * igc_handle_autoneg_disabled - Configure forced speed/duplex settings + * @adapter: private driver structure + * @speed: requested speed (must be SPEED_10 or SPEED_100) + * @duplex: requested duplex + * + * Records forced speed/duplex when autoneg is disabled. + * Caller must validate speed before calling this function. + */ +static void igc_handle_autoneg_disabled(struct igc_adapter *adapter, u32 speed, + u8 duplex) { - struct igc_adapter *adapter = netdev_priv(netdev); - struct net_device *dev = adapter->netdev; - struct igc_hw *hw = &adapter->hw; - u16 advertised = 0; + struct igc_mac_info *mac = &adapter->hw.mac; - /* When adapter in resetting mode, autoneg/speed/duplex - * cannot be changed - */ - if (igc_check_reset_block(hw)) { - netdev_err(dev, "Cannot change link characteristics when reset is active\n"); - return -EINVAL; + switch (speed) { + case SPEED_10: + mac->forced_speed_duplex = (duplex == DUPLEX_FULL) ? + IGC_FORCED_10F : IGC_FORCED_10H; + break; + case SPEED_100: + mac->forced_speed_duplex = (duplex == DUPLEX_FULL) ? + IGC_FORCED_100F : IGC_FORCED_100H; + break; + default: + WARN_ONCE(1, "Unsupported speed %u\n", speed); + return; } - /* MDI setting is only allowed when autoneg enabled because - * some hardware doesn't allow MDI setting when speed or - * duplex is forced. - */ - if (cmd->base.eth_tp_mdix_ctrl) { - if (cmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_AUTO && - cmd->base.autoneg != AUTONEG_ENABLE) { - netdev_err(dev, "Forcing MDI/MDI-X state is not supported when link speed and/or duplex are forced\n"); - return -EINVAL; - } - } + mac->autoneg_enabled = false; +} - while (test_and_set_bit(__IGC_RESETTING, &adapter->state)) - usleep_range(1000, 2000); +/** + * igc_handle_autoneg_enabled - Configure autonegotiation advertisement + * @adapter: private driver structure + * @cmd: ethtool link ksettings from user + * + * Records advertised speeds and flow control settings when autoneg + * is enabled. + */ +static void igc_handle_autoneg_enabled(struct igc_adapter *adapter, + const struct ethtool_link_ksettings *cmd) +{ + struct igc_hw *hw = &adapter->hw; + u16 advertised = 0; if (ethtool_link_ksettings_test_link_mode(cmd, advertising, 2500baseT_Full)) @@ -2056,14 +2114,66 @@ igc_ethtool_set_link_ksettings(struct net_device *netdev, 10baseT_Half)) advertised |= ADVERTISE_10_HALF; - if (cmd->base.autoneg == AUTONEG_ENABLE) { - hw->phy.autoneg_advertised = advertised; - if (adapter->fc_autoneg) - hw->fc.requested_mode = igc_fc_default; - } else { - netdev_info(dev, "Force mode currently not supported\n"); + hw->mac.autoneg_enabled = true; + hw->phy.autoneg_advertised = advertised; + if (adapter->fc_autoneg) + hw->fc.requested_mode = igc_fc_default; +} + +static int +igc_ethtool_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) +{ + struct igc_adapter *adapter = netdev_priv(netdev); + struct net_device *dev = adapter->netdev; + struct igc_hw *hw = &adapter->hw; + + /* When adapter in resetting mode, autoneg/speed/duplex + * cannot be changed + */ + if (igc_check_reset_block(hw)) { + netdev_err(dev, "Cannot change link characteristics when reset is active\n"); + return -EINVAL; + } + + if (cmd->base.autoneg != AUTONEG_ENABLE && + cmd->base.autoneg != AUTONEG_DISABLE) { + netdev_info(dev, "Unsupported autoneg setting\n"); + return -EINVAL; + } + + /* MDI setting is only allowed when autoneg enabled because + * some hardware doesn't allow MDI setting when speed or + * duplex is forced. + */ + if (cmd->base.eth_tp_mdix_ctrl) { + if (cmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_AUTO && + cmd->base.autoneg != AUTONEG_ENABLE) { + netdev_err(dev, "Forcing MDI/MDI-X state is not supported when link speed and/or duplex are forced\n"); + return -EINVAL; + } } + if (cmd->base.autoneg == AUTONEG_DISABLE) { + if (cmd->base.speed != SPEED_10 && cmd->base.speed != SPEED_100) { + netdev_info(dev, "Unsupported speed for forced link\n"); + return -EINVAL; + } + if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL) { + netdev_info(dev, "Duplex must be half or full for forced link\n"); + return -EINVAL; + } + } + + while (test_and_set_bit(__IGC_RESETTING, &adapter->state)) + usleep_range(1000, 2000); + + if (cmd->base.autoneg == AUTONEG_ENABLE) + igc_handle_autoneg_enabled(adapter, cmd); + else + igc_handle_autoneg_disabled(adapter, cmd->base.speed, + cmd->base.duplex); + /* MDI-X => 2; MDI => 1; Auto => 3 */ if (cmd->base.eth_tp_mdix_ctrl) { /* fix up the value for auto (3 => 0) as zero is mapped @@ -2175,6 +2285,7 @@ static const struct ethtool_ops igc_ethtool_ops = { .get_rxnfc = igc_ethtool_get_rxnfc, .set_rxnfc = igc_ethtool_set_rxnfc, .get_rx_ring_count = igc_ethtool_get_rx_ring_count, + .get_rxfh_key_size = igc_ethtool_get_rxfh_key_size, .get_rxfh_indir_size = igc_ethtool_get_rxfh_indir_size, .get_rxfh = igc_ethtool_get_rxfh, .set_rxfh = igc_ethtool_set_rxfh, |
