Merge branch '1GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue

Tony Nguyen says:

====================
Intel Wired LAN Driver Updates 2026-07-01 (igc, igb)

Kohei Enju adds ethtool support for get/set hash key on igc and adds
setting of skb hash type based on values from Rx descriptor on igb.

Takashi Kozu adds ethtool support for get/set hash key on igb.

Faizal adds support for forcing link speed via ethtool when
autonegotiation is disabled on the igc driver.

* '1GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue:
  igc: add support for forcing link speed without autonegotiation
  igc: replace goto out with direct returns in igc_config_fc_after_link_up()
  igc: move autoneg-enabled settings into igc_handle_autoneg_enabled()
  igc: remove unused autoneg_failed field
  igb: set skb hash type from RSS_TYPE
  igb: allow configuring RSS key via ethtool set_rxfh
  igb: expose RSS key via ethtool get_rxfh
  igb: prepare for RSS key get/set support
  igc: allow configuring RSS key via ethtool set_rxfh
  igc: expose RSS key via ethtool get_rxfh
  igc: prepare for RSS key get/set support
====================

Link: https://patch.msgid.link/20260701210303.1745310-1-anthony.l.nguyen@intel.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2026-07-08 14:30:45 +02:00
commit 08030ddb87
13 changed files with 427 additions and 156 deletions

View File

@ -87,6 +87,27 @@ union e1000_adv_rx_desc {
} wb; /* writeback */
};
#define E1000_RSS_TYPE_NO_HASH 0
#define E1000_RSS_TYPE_HASH_TCP_IPV4 1
#define E1000_RSS_TYPE_HASH_IPV4 2
#define E1000_RSS_TYPE_HASH_TCP_IPV6 3
#define E1000_RSS_TYPE_HASH_IPV6_EX 4
#define E1000_RSS_TYPE_HASH_IPV6 5
#define E1000_RSS_TYPE_HASH_TCP_IPV6_EX 6
#define E1000_RSS_TYPE_HASH_UDP_IPV4 7
#define E1000_RSS_TYPE_HASH_UDP_IPV6 8
#define E1000_RSS_TYPE_HASH_UDP_IPV6_EX 9
#define E1000_RSS_TYPE_MASK GENMASK(3, 0)
#define E1000_RSS_L4_TYPES_MASK \
(BIT(E1000_RSS_TYPE_HASH_TCP_IPV4) | \
BIT(E1000_RSS_TYPE_HASH_TCP_IPV6) | \
BIT(E1000_RSS_TYPE_HASH_TCP_IPV6_EX) | \
BIT(E1000_RSS_TYPE_HASH_UDP_IPV4) | \
BIT(E1000_RSS_TYPE_HASH_UDP_IPV6) | \
BIT(E1000_RSS_TYPE_HASH_UDP_IPV6_EX))
#define E1000_RXDADV_HDRBUFLEN_MASK 0x7FE0
#define E1000_RXDADV_HDRBUFLEN_SHIFT 5
#define E1000_RXDADV_STAT_TS 0x10000 /* Pkt was time stamped */

View File

@ -495,6 +495,7 @@ struct hwmon_buff {
#define IGB_N_PEROUT 2
#define IGB_N_SDP 4
#define IGB_RETA_SIZE 128
#define IGB_RSS_KEY_SIZE 40
enum igb_filter_match_flags {
IGB_FILTER_FLAG_ETHER_TYPE = 0x1,
@ -655,6 +656,7 @@ struct igb_adapter {
struct i2c_client *i2c_client;
u32 rss_indir_tbl_init;
u8 rss_indir_tbl[IGB_RETA_SIZE];
u8 rss_key[IGB_RSS_KEY_SIZE];
unsigned long link_check_timeout;
int copper_tries;
@ -735,6 +737,7 @@ void igb_down(struct igb_adapter *);
void igb_reinit_locked(struct igb_adapter *);
void igb_reset(struct igb_adapter *);
int igb_reinit_queues(struct igb_adapter *);
void igb_write_rss_key(struct igb_adapter *adapter);
void igb_write_rss_indir_tbl(struct igb_adapter *);
int igb_set_spd_dplx(struct igb_adapter *, u32, u8);
int igb_setup_tx_resources(struct igb_ring *);

View File

@ -3019,6 +3019,27 @@ static int igb_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
return ret;
}
/**
* igb_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 E1000 hardware registers.
* Each 32-bit chunk of the key is read using get_unaligned_le32() and written
* to the appropriate register.
*/
void igb_write_rss_key(struct igb_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
ASSERT_RTNL();
for (int i = 0; i < IGB_RSS_KEY_SIZE / 4; i++) {
u32 val = get_unaligned_le32(&adapter->rss_key[i * 4]);
wr32(E1000_RSSRK(i), val);
}
}
static int igb_get_eee(struct net_device *netdev, struct ethtool_keee *edata)
{
struct igb_adapter *adapter = netdev_priv(netdev);
@ -3276,10 +3297,12 @@ static int igb_get_rxfh(struct net_device *netdev,
int i;
rxfh->hfunc = ETH_RSS_HASH_TOP;
if (!rxfh->indir)
return 0;
for (i = 0; i < IGB_RETA_SIZE; i++)
rxfh->indir[i] = adapter->rss_indir_tbl[i];
if (rxfh->indir)
for (i = 0; i < IGB_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;
}
@ -3319,6 +3342,11 @@ void igb_write_rss_indir_tbl(struct igb_adapter *adapter)
}
}
static u32 igb_get_rxfh_key_size(struct net_device *netdev)
{
return IGB_RSS_KEY_SIZE;
}
static int igb_set_rxfh(struct net_device *netdev,
struct ethtool_rxfh_param *rxfh,
struct netlink_ext_ack *extack)
@ -3329,35 +3357,39 @@ static int igb_set_rxfh(struct net_device *netdev,
u32 num_queues;
/* 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;
switch (hw->mac.type) {
case e1000_82576:
/* 82576 supports 2 RSS queues for SR-IOV */
if (adapter->vfs_allocated_count)
num_queues = 2;
break;
default:
break;
switch (hw->mac.type) {
case e1000_82576:
/* 82576 supports 2 RSS queues for SR-IOV */
if (adapter->vfs_allocated_count)
num_queues = 2;
break;
default:
break;
}
/* Verify user input. */
for (i = 0; i < IGB_RETA_SIZE; i++)
if (rxfh->indir[i] >= num_queues)
return -EINVAL;
for (i = 0; i < IGB_RETA_SIZE; i++)
adapter->rss_indir_tbl[i] = rxfh->indir[i];
igb_write_rss_indir_tbl(adapter);
}
/* Verify user input. */
for (i = 0; i < IGB_RETA_SIZE; i++)
if (rxfh->indir[i] >= num_queues)
return -EINVAL;
for (i = 0; i < IGB_RETA_SIZE; i++)
adapter->rss_indir_tbl[i] = rxfh->indir[i];
igb_write_rss_indir_tbl(adapter);
if (rxfh->key) {
memcpy(adapter->rss_key, rxfh->key, sizeof(adapter->rss_key));
igb_write_rss_key(adapter);
}
return 0;
}
@ -3483,6 +3515,7 @@ static const struct ethtool_ops igb_ethtool_ops = {
.get_module_eeprom = igb_get_module_eeprom,
.get_rxfh_indir_size = igb_get_rxfh_indir_size,
.get_rxfh = igb_get_rxfh,
.get_rxfh_key_size = igb_get_rxfh_key_size,
.set_rxfh = igb_set_rxfh,
.get_rxfh_fields = igb_get_rxfh_fields,
.set_rxfh_fields = igb_set_rxfh_fields,

View File

@ -4048,6 +4048,9 @@ static int igb_sw_init(struct igb_adapter *adapter)
pci_read_config_word(pdev, PCI_COMMAND, &hw->bus.pci_cmd_word);
/* init RSS key */
netdev_rss_key_fill(adapter->rss_key, sizeof(adapter->rss_key));
/* set default ring sizes */
adapter->tx_ring_count = IGB_DEFAULT_TXD;
adapter->rx_ring_count = IGB_DEFAULT_RXD;
@ -4522,11 +4525,8 @@ static void igb_setup_mrqc(struct igb_adapter *adapter)
struct e1000_hw *hw = &adapter->hw;
u32 mrqc, rxcsum;
u32 j, num_rx_queues;
u32 rss_key[10];
netdev_rss_key_fill(rss_key, sizeof(rss_key));
for (j = 0; j < 10; j++)
wr32(E1000_RSSRK(j), rss_key[j]);
igb_write_rss_key(adapter);
num_rx_queues = adapter->rss_queues;
@ -8820,10 +8820,19 @@ static inline void igb_rx_hash(struct igb_ring *ring,
union e1000_adv_rx_desc *rx_desc,
struct sk_buff *skb)
{
if (ring->netdev->features & NETIF_F_RXHASH)
skb_set_hash(skb,
le32_to_cpu(rx_desc->wb.lower.hi_dword.rss),
PKT_HASH_TYPE_L3);
u16 rss_type;
if (!(ring->netdev->features & NETIF_F_RXHASH))
return;
rss_type = le16_to_cpu(rx_desc->wb.lower.lo_dword.pkt_info) &
E1000_RSS_TYPE_MASK;
if (!rss_type)
return;
skb_set_hash(skb, le32_to_cpu(rx_desc->wb.lower.hi_dword.rss),
(E1000_RSS_L4_TYPES_MASK & BIT(rss_type)) ?
PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3);
}
/**

View File

@ -30,6 +30,7 @@ void igc_ethtool_set_ops(struct net_device *);
#define MAX_ETYPE_FILTER 8
#define IGC_RETA_SIZE 128
#define IGC_RSS_KEY_SIZE 40
/* SDP support */
#define IGC_N_EXTTS 2
@ -302,6 +303,7 @@ struct igc_adapter {
unsigned int nfc_rule_count;
u8 rss_indir_tbl[IGC_RETA_SIZE];
u8 rss_key[IGC_RSS_KEY_SIZE];
unsigned long link_check_timeout;
struct igc_info ei;
@ -361,6 +363,7 @@ unsigned int igc_get_max_rss_queues(struct igc_adapter *adapter);
void igc_set_flag_queue_pairs(struct igc_adapter *adapter,
const u32 max_rss_queues);
int igc_reinit_queues(struct igc_adapter *adapter);
void igc_write_rss_key(struct igc_adapter *adapter);
void igc_write_rss_indir_tbl(struct igc_adapter *adapter);
bool igc_has_link(struct igc_adapter *adapter);
void igc_reset(struct igc_adapter *adapter);

View File

@ -114,11 +114,35 @@ static s32 igc_setup_copper_link_base(struct igc_hw *hw)
u32 ctrl;
ctrl = rd32(IGC_CTRL);
ctrl |= IGC_CTRL_SLU;
ctrl &= ~(IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX);
wr32(IGC_CTRL, ctrl);
ctrl &= ~(IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX |
IGC_CTRL_SPEED_MASK | IGC_CTRL_FD);
ret_val = igc_setup_copper_link(hw);
if (hw->mac.autoneg_enabled) {
ctrl |= IGC_CTRL_SLU;
wr32(IGC_CTRL, ctrl);
ret_val = igc_setup_copper_link(hw);
} else {
ctrl |= IGC_CTRL_SLU | IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX;
switch (hw->mac.forced_speed_duplex) {
case IGC_FORCED_10H:
ctrl |= IGC_CTRL_SPEED_10;
break;
case IGC_FORCED_10F:
ctrl |= IGC_CTRL_SPEED_10 | IGC_CTRL_FD;
break;
case IGC_FORCED_100H:
ctrl |= IGC_CTRL_SPEED_100;
break;
case IGC_FORCED_100F:
ctrl |= IGC_CTRL_SPEED_100 | IGC_CTRL_FD;
break;
default:
return -IGC_ERR_CONFIG;
}
wr32(IGC_CTRL, ctrl);
ret_val = igc_setup_copper_link(hw);
}
return ret_val;
}
@ -443,6 +467,7 @@ static const struct igc_phy_operations igc_phy_ops_base = {
.reset = igc_phy_hw_reset,
.read_reg = igc_read_phy_reg_gpy,
.write_reg = igc_write_phy_reg_gpy,
.force_speed_duplex = igc_force_speed_duplex,
};
const struct igc_info igc_base_info = {

View File

@ -129,10 +129,13 @@
#define IGC_ERR_SWFW_SYNC 13
/* Device Control */
#define IGC_CTRL_FD BIT(0) /* Full Duplex */
#define IGC_CTRL_RST 0x04000000 /* Global reset */
#define IGC_CTRL_PHY_RST 0x80000000 /* PHY Reset */
#define IGC_CTRL_SLU 0x00000040 /* Set link up (Force Link) */
#define IGC_CTRL_SPEED_MASK GENMASK(10, 8)
#define IGC_CTRL_SPEED_10 FIELD_PREP(IGC_CTRL_SPEED_MASK, 0)
#define IGC_CTRL_SPEED_100 FIELD_PREP(IGC_CTRL_SPEED_MASK, 1)
#define IGC_CTRL_FRCSPD 0x00000800 /* Force Speed */
#define IGC_CTRL_FRCDPX 0x00001000 /* Force Duplex */
#define IGC_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */
@ -673,6 +676,10 @@
#define IGC_GEN_POLL_TIMEOUT 1920
/* PHY Control Register */
#define MII_CR_SPEED_MASK (BIT(6) | BIT(13))
#define MII_CR_SPEED_10 0x0000 /* SSM=0, SSL=0: 10 Mb/s */
#define MII_CR_SPEED_100 BIT(13) /* SSM=0, SSL=1: 100 Mb/s */
#define MII_CR_DUPLEX_EN BIT(8) /* 0 = Half Duplex, 1 = Full Duplex */
#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */
#define MII_CR_POWER_DOWN 0x0800 /* Power down */
#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */

View File

@ -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;
/* Set pause flow control settings */
ethtool_link_ksettings_add_link_mode(cmd, supported, Pause);
/* 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);
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;
/* 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;
}
/* Pause is always supported */
ethtool_link_ksettings_add_link_mode(cmd, supported, Pause);
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,38 +2045,51 @@ 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_mac_info *mac = &adapter->hw.mac;
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;
}
mac->autoneg_enabled = false;
}
/**
* 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_adapter *adapter = netdev_priv(netdev);
struct net_device *dev = adapter->netdev;
struct igc_hw *hw = &adapter->hw;
u16 advertised = 0;
/* 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;
}
/* 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;
}
}
while (test_and_set_bit(__IGC_RESETTING, &adapter->state))
usleep_range(1000, 2000);
if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
2500baseT_Full))
advertised |= ADVERTISE_2500_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,

View File

@ -73,6 +73,13 @@ struct igc_info {
extern const struct igc_info igc_base_info;
enum igc_forced_speed_duplex {
IGC_FORCED_10H,
IGC_FORCED_10F,
IGC_FORCED_100H,
IGC_FORCED_100F,
};
struct igc_mac_info {
struct igc_mac_operations ops;
@ -92,8 +99,9 @@ struct igc_mac_info {
bool asf_firmware_present;
bool arc_subsystem_valid;
bool autoneg_failed;
bool get_link_status;
bool autoneg_enabled;
enum igc_forced_speed_duplex forced_speed_duplex;
};
struct igc_nvm_operations {

View File

@ -438,26 +438,23 @@ void igc_config_collision_dist(struct igc_hw *hw)
* Checks the status of auto-negotiation after link up to ensure that the
* speed and duplex were not forced. If the link needed to be forced, then
* flow control needs to be forced also. If auto-negotiation is enabled
* and did not fail, then we configure flow control based on our link
* partner.
* then we configure flow control based on our link partner.
*/
s32 igc_config_fc_after_link_up(struct igc_hw *hw)
{
u16 mii_status_reg, mii_nway_adv_reg, mii_nway_lp_ability_reg;
struct igc_mac_info *mac = &hw->mac;
u16 speed, duplex;
s32 ret_val = 0;
/* Check for the case where we have fiber media and auto-neg failed
* so we had to force link. In this case, we need to force the
* configuration of the MAC to match the "fc" parameter.
/* Without autoneg, flow control capability is not exchanged with the
* link partner. IEEE 802.3 prohibits flow control in half-duplex mode.
*/
if (mac->autoneg_failed)
ret_val = igc_force_mac_fc(hw);
if (!hw->mac.autoneg_enabled) {
if (hw->mac.forced_speed_duplex == IGC_FORCED_10H ||
hw->mac.forced_speed_duplex == IGC_FORCED_100H)
hw->fc.current_mode = igc_fc_none;
if (ret_val) {
hw_dbg("Error forcing flow control settings\n");
goto out;
goto force_fc;
}
/* In auto-neg, we need to check and see if Auto-Neg has completed,
@ -472,15 +469,15 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS,
&mii_status_reg);
if (ret_val)
goto out;
return ret_val;
ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS,
&mii_status_reg);
if (ret_val)
goto out;
return ret_val;
if (!(mii_status_reg & MII_SR_AUTONEG_COMPLETE)) {
hw_dbg("Copper PHY and Auto Neg has not completed.\n");
goto out;
return ret_val;
}
/* The AutoNeg process has completed, so we now need to
@ -492,11 +489,11 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
ret_val = hw->phy.ops.read_reg(hw, PHY_AUTONEG_ADV,
&mii_nway_adv_reg);
if (ret_val)
goto out;
return ret_val;
ret_val = hw->phy.ops.read_reg(hw, PHY_LP_ABILITY,
&mii_nway_lp_ability_reg);
if (ret_val)
goto out;
return ret_val;
/* Two bits in the Auto Negotiation Advertisement Register
* (Address 4) and two bits in the Auto Negotiation Base
* Page Ability Register (Address 5) determine flow control
@ -612,7 +609,7 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
ret_val = hw->mac.ops.get_speed_and_duplex(hw, &speed, &duplex);
if (ret_val) {
hw_dbg("Error getting link speed and duplex\n");
goto out;
return ret_val;
}
if (duplex == HALF_DUPLEX)
@ -621,13 +618,13 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
/* Now we call a subroutine to actually force the MAC
* controller to use the correct flow control settings.
*/
force_fc:
ret_val = igc_force_mac_fc(hw);
if (ret_val) {
hw_dbg("Error forcing flow control settings\n");
goto out;
return ret_val;
}
out:
return ret_val;
}

View File

@ -785,11 +785,8 @@ static void igc_setup_mrqc(struct igc_adapter *adapter)
struct igc_hw *hw = &adapter->hw;
u32 j, num_rx_queues;
u32 mrqc, rxcsum;
u32 rss_key[10];
netdev_rss_key_fill(rss_key, sizeof(rss_key));
for (j = 0; j < 10; j++)
wr32(IGC_RSSRK(j), rss_key[j]);
igc_write_rss_key(adapter);
num_rx_queues = adapter->rss_queues;
@ -5048,6 +5045,9 @@ static int igc_sw_init(struct igc_adapter *adapter)
pci_read_config_word(pdev, PCI_COMMAND, &hw->bus.pci_cmd_word);
/* init RSS key */
netdev_rss_key_fill(adapter->rss_key, sizeof(adapter->rss_key));
/* set default ring sizes */
adapter->tx_ring_count = IGC_DEFAULT_TXD;
adapter->rx_ring_count = IGC_DEFAULT_RXD;
@ -7298,7 +7298,7 @@ static int igc_probe(struct pci_dev *pdev,
/* Initialize link properties that are user-changeable */
adapter->fc_autoneg = true;
hw->phy.autoneg_advertised = 0xaf;
hw->mac.autoneg_enabled = true;
hw->fc.requested_mode = igc_fc_default;
hw->fc.current_mode = igc_fc_default;

View File

@ -494,12 +494,20 @@ s32 igc_setup_copper_link(struct igc_hw *hw)
s32 ret_val = 0;
bool link;
/* Setup autoneg and flow control advertisement and perform
* autonegotiation.
*/
ret_val = igc_copper_link_autoneg(hw);
if (ret_val)
goto out;
if (hw->mac.autoneg_enabled) {
/* Setup autoneg and flow control advertisement and perform
* autonegotiation.
*/
ret_val = igc_copper_link_autoneg(hw);
if (ret_val)
goto out;
} else {
ret_val = hw->phy.ops.force_speed_duplex(hw);
if (ret_val) {
hw_dbg("Error Forcing Speed/Duplex\n");
goto out;
}
}
/* Check link status. Wait up to 100 microseconds for link to become
* valid.
@ -778,3 +786,48 @@ u16 igc_read_phy_fw_version(struct igc_hw *hw)
return gphy_version;
}
/**
* igc_force_speed_duplex - Force PHY speed and duplex settings
* @hw: pointer to the HW structure
*
* Programs the GPY PHY control register to disable autonegotiation
* and force the speed/duplex indicated by hw->mac.forced_speed_duplex.
*/
s32 igc_force_speed_duplex(struct igc_hw *hw)
{
struct igc_phy_info *phy = &hw->phy;
u16 phy_ctrl;
s32 ret_val;
ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl);
if (ret_val)
return ret_val;
phy_ctrl &= ~(MII_CR_SPEED_MASK | MII_CR_DUPLEX_EN |
MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG);
switch (hw->mac.forced_speed_duplex) {
case IGC_FORCED_10H:
phy_ctrl |= MII_CR_SPEED_10;
break;
case IGC_FORCED_10F:
phy_ctrl |= MII_CR_SPEED_10 | MII_CR_DUPLEX_EN;
break;
case IGC_FORCED_100H:
phy_ctrl |= MII_CR_SPEED_100;
break;
case IGC_FORCED_100F:
phy_ctrl |= MII_CR_SPEED_100 | MII_CR_DUPLEX_EN;
break;
default:
return -IGC_ERR_CONFIG;
}
ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_ctrl);
if (ret_val)
return ret_val;
hw->mac.get_link_status = true;
return 0;
}

View File

@ -18,5 +18,6 @@ void igc_power_down_phy_copper(struct igc_hw *hw);
s32 igc_write_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 data);
s32 igc_read_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 *data);
u16 igc_read_phy_fw_version(struct igc_hw *hw);
s32 igc_force_speed_duplex(struct igc_hw *hw);
#endif