summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/intel/igc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/intel/igc')
-rw-r--r--drivers/net/ethernet/intel/igc/igc.h6
-rw-r--r--drivers/net/ethernet/intel/igc/igc_base.c35
-rw-r--r--drivers/net/ethernet/intel/igc/igc_defines.h14
-rw-r--r--drivers/net/ethernet/intel/igc/igc_diag.c2
-rw-r--r--drivers/net/ethernet/intel/igc/igc_ethtool.c283
-rw-r--r--drivers/net/ethernet/intel/igc/igc_hw.h10
-rw-r--r--drivers/net/ethernet/intel/igc/igc_leds.c2
-rw-r--r--drivers/net/ethernet/intel/igc/igc_mac.c35
-rw-r--r--drivers/net/ethernet/intel/igc/igc_main.c133
-rw-r--r--drivers/net/ethernet/intel/igc/igc_phy.c71
-rw-r--r--drivers/net/ethernet/intel/igc/igc_phy.h1
-rw-r--r--drivers/net/ethernet/intel/igc/igc_ptp.c83
-rw-r--r--drivers/net/ethernet/intel/igc/igc_tsn.c17
13 files changed, 486 insertions, 206 deletions
diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h
index a427f05814c1..17f213cc93e4 100644
--- a/drivers/net/ethernet/intel/igc/igc.h
+++ b/drivers/net/ethernet/intel/igc/igc.h
@@ -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;
@@ -326,6 +328,7 @@ struct igc_adapter {
struct timespec64 prev_ptp_time; /* Pre-reset PTP clock */
ktime_t ptp_reset_start; /* Reset time in clock mono */
struct system_time_snapshot snapshot;
+ clockid_t snapshot_clock_id;
struct mutex ptm_lock; /* Only allow one PTM transaction at a time */
char fw_version[32];
@@ -360,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);
@@ -781,6 +785,8 @@ int igc_ptp_hwtstamp_set(struct net_device *netdev,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack);
void igc_ptp_tx_hang(struct igc_adapter *adapter);
+void igc_ptp_clear_xsk_tx_tstamp_queue(struct igc_adapter *adapter,
+ u16 queue_id);
void igc_ptp_read(struct igc_adapter *adapter, struct timespec64 *ts);
void igc_ptp_tx_tstamp_event(struct igc_adapter *adapter);
diff --git a/drivers/net/ethernet/intel/igc/igc_base.c b/drivers/net/ethernet/intel/igc/igc_base.c
index 1613b562d17c..ab9120a3127f 100644
--- a/drivers/net/ethernet/intel/igc/igc_base.c
+++ b/drivers/net/ethernet/intel/igc/igc_base.c
@@ -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);
-
- ret_val = igc_setup_copper_link(hw);
+ ctrl &= ~(IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX |
+ IGC_CTRL_SPEED_MASK | IGC_CTRL_FD);
+
+ 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 = {
diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h
index 498ba1522ca4..3f504751c2d9 100644
--- a/drivers/net/ethernet/intel/igc/igc_defines.h
+++ b/drivers/net/ethernet/intel/igc/igc_defines.h
@@ -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 */
@@ -443,9 +446,10 @@
#define IGC_TXPBSIZE_DEFAULT ( \
IGC_TXPB0SIZE(20) | IGC_TXPB1SIZE(0) | IGC_TXPB2SIZE(0) | \
IGC_TXPB3SIZE(0) | IGC_OS2BMCPBSIZE(4))
+/* TSN value following I225/I226 SW User Manual Section 7.5.4 */
#define IGC_TXPBSIZE_TSN ( \
- IGC_TXPB0SIZE(7) | IGC_TXPB1SIZE(7) | IGC_TXPB2SIZE(7) | \
- IGC_TXPB3SIZE(7) | IGC_OS2BMCPBSIZE(4))
+ IGC_TXPB0SIZE(5) | IGC_TXPB1SIZE(5) | IGC_TXPB2SIZE(5) | \
+ IGC_TXPB3SIZE(5) | IGC_OS2BMCPBSIZE(4))
#define IGC_DTXMXPKTSZ_TSN 0x19 /* 1600 bytes of max TX DMA packet size */
#define IGC_DTXMXPKTSZ_DEFAULT 0x98 /* 9728-byte Jumbo frames */
@@ -672,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 */
diff --git a/drivers/net/ethernet/intel/igc/igc_diag.c b/drivers/net/ethernet/intel/igc/igc_diag.c
index a43d7244ee70..031561fdce49 100644
--- a/drivers/net/ethernet/intel/igc/igc_diag.c
+++ b/drivers/net/ethernet/intel/igc/igc_diag.c
@@ -172,7 +172,7 @@ bool igc_link_test(struct igc_adapter *adapter, u64 *data)
*data = 0;
- /* add delay to give enough time for autonegotioation to finish */
+ /* add delay to give enough time for autonegotiation to finish */
ssleep(5);
link_up = igc_has_link(adapter);
diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c
index e94c1922b97a..89fe2788a565 100644
--- a/drivers/net/ethernet/intel/igc/igc_ethtool.c
+++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c
@@ -1395,7 +1395,7 @@ static int igc_ethtool_add_nfc_rule(struct igc_adapter *adapter,
return -EINVAL;
}
- rule = kzalloc(sizeof(*rule), GFP_KERNEL);
+ rule = kzalloc_obj(*rule);
if (!rule)
return -ENOMEM;
@@ -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;
}
@@ -1565,8 +1597,8 @@ static int igc_ethtool_set_channels(struct net_device *netdev,
if (ch->other_count != NON_Q_VECTORS)
return -EINVAL;
- /* Do not allow channel reconfiguration when mqprio is enabled */
- if (adapter->strict_priority_enable)
+ /* Do not allow channel reconfiguration when any TSN qdisc is enabled */
+ if (adapter->flags & IGC_FLAG_TSN_ANY_ENABLED)
return -EINVAL;
/* Verify the number of channels doesn't exceed hw limits */
@@ -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,
diff --git a/drivers/net/ethernet/intel/igc/igc_hw.h b/drivers/net/ethernet/intel/igc/igc_hw.h
index be8a49a86d09..62aaee55668a 100644
--- a/drivers/net/ethernet/intel/igc/igc_hw.h
+++ b/drivers/net/ethernet/intel/igc/igc_hw.h
@@ -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 {
diff --git a/drivers/net/ethernet/intel/igc/igc_leds.c b/drivers/net/ethernet/intel/igc/igc_leds.c
index 3929b25b6ae6..fdb9692516e8 100644
--- a/drivers/net/ethernet/intel/igc/igc_leds.c
+++ b/drivers/net/ethernet/intel/igc/igc_leds.c
@@ -268,7 +268,7 @@ int igc_led_setup(struct igc_adapter *adapter)
mutex_init(&adapter->led_mutex);
- leds = kcalloc(IGC_NUM_LEDS, sizeof(*leds), GFP_KERNEL);
+ leds = kzalloc_objs(*leds, IGC_NUM_LEDS);
if (!leds)
return -ENOMEM;
diff --git a/drivers/net/ethernet/intel/igc/igc_mac.c b/drivers/net/ethernet/intel/igc/igc_mac.c
index 7ac6637f8db7..d6f3f6618469 100644
--- a/drivers/net/ethernet/intel/igc/igc_mac.c
+++ b/drivers/net/ethernet/intel/igc/igc_mac.c
@@ -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;
}
diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
index 7aafa60ba0c8..1fb5f3cbe93c 100644
--- a/drivers/net/ethernet/intel/igc/igc_main.c
+++ b/drivers/net/ethernet/intel/igc/igc_main.c
@@ -47,24 +47,24 @@ static const struct igc_info *igc_info_tbl[] = {
};
static const struct pci_device_id igc_pci_tbl[] = {
- { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_LM), board_base },
- { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_V), board_base },
- { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_I), board_base },
- { PCI_VDEVICE(INTEL, IGC_DEV_ID_I220_V), board_base },
- { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_K), board_base },
- { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_K2), board_base },
- { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_K), board_base },
- { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_LMVP), board_base },
- { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_LMVP), board_base },
- { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_IT), board_base },
- { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_LM), board_base },
- { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_V), board_base },
- { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_IT), board_base },
- { PCI_VDEVICE(INTEL, IGC_DEV_ID_I221_V), board_base },
- { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_BLANK_NVM), board_base },
- { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_BLANK_NVM), board_base },
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_LM), .driver_data = board_base },
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_V), .driver_data = board_base },
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_I), .driver_data = board_base },
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I220_V), .driver_data = board_base },
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_K), .driver_data = board_base },
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_K2), .driver_data = board_base },
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_K), .driver_data = board_base },
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_LMVP), .driver_data = board_base },
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_LMVP), .driver_data = board_base },
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_IT), .driver_data = board_base },
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_LM), .driver_data = board_base },
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_V), .driver_data = board_base },
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_IT), .driver_data = board_base },
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I221_V), .driver_data = board_base },
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_BLANK_NVM), .driver_data = board_base },
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_BLANK_NVM), .driver_data = board_base },
/* required last entry */
- {0, }
+ { }
};
MODULE_DEVICE_TABLE(pci, igc_pci_tbl);
@@ -264,6 +264,13 @@ static void igc_clean_tx_ring(struct igc_ring *tx_ring)
/* reset next_to_use and next_to_clean */
tx_ring->next_to_use = 0;
tx_ring->next_to_clean = 0;
+
+ /* Clear any lingering XSK TX timestamp requests */
+ if (test_bit(IGC_RING_FLAG_TX_HWTSTAMP, &tx_ring->flags)) {
+ struct igc_adapter *adapter = netdev_priv(tx_ring->netdev);
+
+ igc_ptp_clear_xsk_tx_tstamp_queue(adapter, tx_ring->queue_index);
+ }
}
/**
@@ -778,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;
@@ -1730,11 +1734,8 @@ static netdev_tx_t igc_xmit_frame(struct sk_buff *skb,
/* The minimum packet size with TCTL.PSP set is 17 so pad the skb
* in order to meet this minimum size requirement.
*/
- if (skb->len < 17) {
- if (skb_padto(skb, 17))
- return NETDEV_TX_OK;
- skb->len = 17;
- }
+ if (skb_put_padto(skb, 17))
+ return NETDEV_TX_OK;
return igc_xmit_frame_ring(skb, igc_tx_queue_mapping(adapter, skb));
}
@@ -1793,7 +1794,7 @@ static const enum pkt_hash_types igc_rss_type_table[IGC_RSS_TYPE_MAX_TABLE] = {
[IGC_RSS_TYPE_HASH_UDP_IPV6_EX] = PKT_HASH_TYPE_L4,
[10] = PKT_HASH_TYPE_NONE, /* RSS Type above 9 "Reserved" by HW */
[11] = PKT_HASH_TYPE_NONE, /* keep array sized for SW bit-mask */
- [12] = PKT_HASH_TYPE_NONE, /* to handle future HW revisons */
+ [12] = PKT_HASH_TYPE_NONE, /* to handle future HW revisions */
[13] = PKT_HASH_TYPE_NONE,
[14] = PKT_HASH_TYPE_NONE,
[15] = PKT_HASH_TYPE_NONE,
@@ -2645,7 +2646,7 @@ static int igc_clean_rx_irq(struct igc_q_vector *q_vector, const int budget)
}
if (igc_fpe_is_pmac_enabled(adapter) &&
- igc_fpe_handle_mpacket(adapter, rx_desc, size, pktbuf)) {
+ igc_fpe_handle_mpacket(adapter, rx_desc, size, pktbuf + pkt_offset)) {
/* Advance the ring next-to-clean */
igc_is_non_eop(rx_ring, rx_desc);
cleaned_count++;
@@ -3070,7 +3071,8 @@ static void igc_xdp_xmit_zc(struct igc_ring *ring)
olinfo_status = xdp_desc.len << IGC_ADVTXD_PAYLEN_SHIFT;
dma = xsk_buff_raw_get_dma(pool, xdp_desc.addr);
- meta = xsk_buff_get_metadata(pool, xdp_desc.addr);
+ meta = xsk_buff_get_metadata(pool, xdp_desc.addr,
+ xdp_desc.options);
xsk_buff_raw_dma_sync_for_device(pool, dma, xdp_desc.len);
bi = &ring->tx_buffer_info[ntu];
@@ -3078,7 +3080,7 @@ static void igc_xdp_xmit_zc(struct igc_ring *ring)
meta_req.tx_buffer = bi;
meta_req.meta = meta;
meta_req.used_desc = 0;
- xsk_tx_metadata_request(meta, &igc_xsk_tx_metadata_ops,
+ xsk_tx_metadata_request(pool, &meta, &igc_xsk_tx_metadata_ops,
&meta_req);
/* xsk_tx_metadata_request() may have updated next_to_use */
@@ -4633,8 +4635,7 @@ static void igc_set_interrupt_capability(struct igc_adapter *adapter,
/* add 1 vector for link status interrupts */
numvecs++;
- adapter->msix_entries = kcalloc(numvecs, sizeof(struct msix_entry),
- GFP_KERNEL);
+ adapter->msix_entries = kzalloc_objs(struct msix_entry, numvecs);
if (!adapter->msix_entries)
return;
@@ -4863,8 +4864,7 @@ static int igc_alloc_q_vector(struct igc_adapter *adapter,
/* allocate q_vector and rings */
q_vector = adapter->q_vector[v_idx];
if (!q_vector)
- q_vector = kzalloc(struct_size(q_vector, ring, ring_count),
- GFP_KERNEL);
+ q_vector = kzalloc_flex(*q_vector, ring, ring_count);
else
memset(q_vector, 0, struct_size(q_vector, ring, ring_count));
if (!q_vector)
@@ -5046,6 +5046,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;
@@ -5350,9 +5353,8 @@ void igc_down(struct igc_adapter *adapter)
for (i = 0; i < adapter->num_q_vectors; i++) {
if (adapter->q_vector[i]) {
- napi_synchronize(&adapter->q_vector[i]->napi);
- igc_set_queue_napi(adapter, i, NULL);
napi_disable(&adapter->q_vector[i]->napi);
+ igc_set_queue_napi(adapter, i, NULL);
}
}
@@ -5686,7 +5688,7 @@ static irqreturn_t igc_msix_ring(int irq, void *data)
/* Write the ITR value calculated from the previous interrupt. */
igc_write_itr(q_vector);
- napi_schedule(&q_vector->napi);
+ napi_schedule_irqoff(&q_vector->napi);
return IRQ_HANDLED;
}
@@ -6057,7 +6059,7 @@ static irqreturn_t igc_intr_msi(int irq, void *data)
if (icr & IGC_ICR_TS)
igc_tsync_interrupt(adapter);
- napi_schedule(&q_vector->napi);
+ napi_schedule_irqoff(&q_vector->napi);
return IRQ_HANDLED;
}
@@ -6103,7 +6105,7 @@ static irqreturn_t igc_intr(int irq, void *data)
if (icr & IGC_ICR_TS)
igc_tsync_interrupt(adapter);
- napi_schedule(&q_vector->napi);
+ napi_schedule_irqoff(&q_vector->napi);
return IRQ_HANDLED;
}
@@ -6908,28 +6910,29 @@ static int igc_xdp_xmit(struct net_device *dev, int num_frames,
return nxmit;
}
-static void igc_trigger_rxtxq_interrupt(struct igc_adapter *adapter,
- struct igc_q_vector *q_vector)
+static u32 igc_sw_irq_prep(struct igc_q_vector *q_vector)
{
- struct igc_hw *hw = &adapter->hw;
u32 eics = 0;
- eics |= q_vector->eims_value;
- wr32(IGC_EICS, eics);
+ if (!napi_if_scheduled_mark_missed(&q_vector->napi))
+ eics = q_vector->eims_value;
+
+ return eics;
}
int igc_xsk_wakeup(struct net_device *dev, u32 queue_id, u32 flags)
{
struct igc_adapter *adapter = netdev_priv(dev);
- struct igc_q_vector *q_vector;
+ struct igc_hw *hw = &adapter->hw;
struct igc_ring *ring;
+ u32 eics = 0;
if (test_bit(__IGC_DOWN, &adapter->state))
return -ENETDOWN;
if (!igc_xdp_is_enabled(adapter))
return -ENXIO;
-
+ /* Check if queue_id is valid. Tx and Rx queue numbers are always same */
if (queue_id >= adapter->num_rx_queues)
return -EINVAL;
@@ -6938,9 +6941,22 @@ int igc_xsk_wakeup(struct net_device *dev, u32 queue_id, u32 flags)
if (!ring->xsk_pool)
return -ENXIO;
- q_vector = adapter->q_vector[queue_id];
- if (!napi_if_scheduled_mark_missed(&q_vector->napi))
- igc_trigger_rxtxq_interrupt(adapter, q_vector);
+ if (flags & XDP_WAKEUP_RX)
+ eics |= igc_sw_irq_prep(ring->q_vector);
+
+ if (flags & XDP_WAKEUP_TX) {
+ /* If IGC_FLAG_QUEUE_PAIRS is active, the q_vector
+ * and NAPI is shared between RX and TX.
+ * If NAPI is already running it would be marked as missed
+ * from the RX path, making this TX call a NOP
+ */
+ ring = adapter->tx_ring[queue_id];
+ eics |= igc_sw_irq_prep(ring->q_vector);
+ }
+
+ if (eics)
+ /* Cause software interrupt */
+ wr32(IGC_EICS, eics);
return 0;
}
@@ -7023,7 +7039,7 @@ static enum xdp_rss_hash_type igc_xdp_rss_type[IGC_RSS_TYPE_MAX_TABLE] = {
[IGC_RSS_TYPE_HASH_UDP_IPV6_EX] = XDP_RSS_TYPE_L4_IPV6_UDP_EX,
[10] = XDP_RSS_TYPE_NONE, /* RSS Type above 9 "Reserved" by HW */
[11] = XDP_RSS_TYPE_NONE, /* keep array sized for SW bit-mask */
- [12] = XDP_RSS_TYPE_NONE, /* to handle future HW revisons */
+ [12] = XDP_RSS_TYPE_NONE, /* to handle future HW revisions */
[13] = XDP_RSS_TYPE_NONE,
[14] = XDP_RSS_TYPE_NONE,
[15] = XDP_RSS_TYPE_NONE,
@@ -7125,7 +7141,7 @@ static int igc_probe(struct pci_dev *pdev,
if (err)
goto err_pci_reg;
- err = pci_enable_ptm(pdev, NULL);
+ err = pci_enable_ptm(pdev);
if (err < 0)
dev_info(&pdev->dev, "PCIe PTM not supported by PCIe bus/controller\n");
@@ -7282,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;
@@ -7570,11 +7586,13 @@ static int __igc_resume(struct device *dev, bool rpm)
err = __igc_open(netdev, true);
if (!rpm)
rtnl_unlock();
- if (!err)
- netif_device_attach(netdev);
+ if (err)
+ return err;
}
- return err;
+ netif_device_attach(netdev);
+
+ return 0;
}
static int igc_resume(struct device *dev)
@@ -7759,6 +7777,11 @@ int igc_reinit_queues(struct igc_adapter *adapter)
if (netif_running(netdev))
err = igc_open(netdev);
+ if (!err) {
+ /* Restore default IEEE 802.1Qbv schedule after queue reinit */
+ igc_tsn_clear_schedule(adapter);
+ }
+
return err;
}
diff --git a/drivers/net/ethernet/intel/igc/igc_phy.c b/drivers/net/ethernet/intel/igc/igc_phy.c
index 6c4d204aecfa..b758a7e0f013 100644
--- a/drivers/net/ethernet/intel/igc/igc_phy.c
+++ b/drivers/net/ethernet/intel/igc/igc_phy.c
@@ -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.
@@ -667,11 +675,7 @@ static s32 __igc_access_xmdio_reg(struct igc_hw *hw, u16 address,
return ret_val;
/* Recalibrate the device back to 0 */
- ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAC, 0);
- if (ret_val)
- return ret_val;
-
- return ret_val;
+ return hw->phy.ops.write_reg(hw, IGC_MMDAC, 0);
}
/**
@@ -778,3 +782,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;
+}
diff --git a/drivers/net/ethernet/intel/igc/igc_phy.h b/drivers/net/ethernet/intel/igc/igc_phy.h
index 832a7e359f18..d37a89174826 100644
--- a/drivers/net/ethernet/intel/igc/igc_phy.h
+++ b/drivers/net/ethernet/intel/igc/igc_phy.h
@@ -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
diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c
index b7b46d863bee..b40aba9ab685 100644
--- a/drivers/net/ethernet/intel/igc/igc_ptp.c
+++ b/drivers/net/ethernet/intel/igc/igc_ptp.c
@@ -550,7 +550,8 @@ static void igc_ptp_free_tx_buffer(struct igc_adapter *adapter,
tstamp->buffer_type = 0;
/* Trigger txrx interrupt for transmit completion */
- igc_xsk_wakeup(adapter->netdev, tstamp->xsk_queue_index, 0);
+ igc_xsk_wakeup(adapter->netdev, tstamp->xsk_queue_index,
+ XDP_WAKEUP_TX);
return;
}
@@ -576,6 +577,39 @@ static void igc_ptp_clear_tx_tstamp(struct igc_adapter *adapter)
spin_unlock_irqrestore(&adapter->ptp_tx_lock, flags);
}
+/**
+ * igc_ptp_clear_xsk_tx_tstamp_queue - Clear pending XSK TX timestamps for a queue
+ * @adapter: Board private structure
+ * @queue_id: TX queue index to clear timestamps for
+ *
+ * Iterates over all TX timestamp registers and releases any pending
+ * timestamp requests associated with the given TX queue. This is
+ * called when an XDP pool is being disabled to ensure no stale
+ * timestamp references remain.
+ */
+void igc_ptp_clear_xsk_tx_tstamp_queue(struct igc_adapter *adapter, u16 queue_id)
+{
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&adapter->ptp_tx_lock, flags);
+
+ for (i = 0; i < IGC_MAX_TX_TSTAMP_REGS; i++) {
+ struct igc_tx_timestamp_request *tstamp = &adapter->tx_tstamp[i];
+
+ if (tstamp->buffer_type != IGC_TX_BUFFER_TYPE_XSK)
+ continue;
+ if (tstamp->xsk_queue_index != queue_id)
+ continue;
+ if (!tstamp->xsk_tx_buffer)
+ continue;
+
+ igc_ptp_free_tx_buffer(adapter, tstamp);
+ }
+
+ spin_unlock_irqrestore(&adapter->ptp_tx_lock, flags);
+}
+
static void igc_ptp_disable_tx_timestamp(struct igc_adapter *adapter)
{
struct igc_hw *hw = &adapter->hw;
@@ -774,36 +808,43 @@ static void igc_ptp_tx_reg_to_stamp(struct igc_adapter *adapter,
static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter)
{
struct igc_hw *hw = &adapter->hw;
+ u32 txstmpl_old;
u64 regval;
u32 mask;
int i;
+ /* Establish baseline of TXSTMPL_0 before checking TXTT_0.
+ * This baseline is used to detect if a new timestamp arrives in
+ * register 0 during the hardware bug workaround below.
+ */
+ txstmpl_old = rd32(IGC_TXSTMPL);
+
mask = rd32(IGC_TSYNCTXCTL) & IGC_TSYNCTXCTL_TXTT_ANY;
if (mask & IGC_TSYNCTXCTL_TXTT_0) {
regval = rd32(IGC_TXSTMPL);
regval |= (u64)rd32(IGC_TXSTMPH) << 32;
} else {
- /* There's a bug in the hardware that could cause
- * missing interrupts for TX timestamping. The issue
- * is that for new interrupts to be triggered, the
- * IGC_TXSTMPH_0 register must be read.
+ /* TXTT_0 not set - register 0 has no new timestamp initially.
+ *
+ * Hardware bug: Future timestamp interrupts won't fire unless
+ * TXSTMPH_0 is read, even if the timestamp was captured in
+ * registers 1-3.
*
- * To avoid discarding a valid timestamp that just
- * happened at the "wrong" time, we need to confirm
- * that there was no timestamp captured, we do that by
- * assuming that no two timestamps in sequence have
- * the same nanosecond value.
+ * Workaround: Read TXSTMPH_0 here to enable future interrupts.
+ * However, this read clears TXTT_0. If a timestamp arrives in
+ * register 0 after checking TXTT_0 but before this read, it
+ * would be lost.
*
- * So, we read the "low" register, read the "high"
- * register (to latch a new timestamp) and read the
- * "low" register again, if "old" and "new" versions
- * of the "low" register are different, a valid
- * timestamp was captured, we can read the "high"
- * register again.
+ * To detect this race: We saved a baseline read of TXSTMPL_0
+ * before TXTT_0 check. After performing the workaround read of
+ * TXSTMPH_0, we read TXSTMPL_0 again. Since consecutive
+ * timestamps never share the same nanosecond value, a change
+ * between the baseline and new TXSTMPL_0 indicates a timestamp
+ * arrived during the race window. If so, read the complete
+ * timestamp.
*/
- u32 txstmpl_old, txstmpl_new;
+ u32 txstmpl_new;
- txstmpl_old = rd32(IGC_TXSTMPL);
rd32(IGC_TXSTMPH);
txstmpl_new = rd32(IGC_TXSTMPL);
@@ -818,7 +859,7 @@ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter)
done:
/* Now that the problematic first register was handled, we can
- * use retrieve the timestamps from the other registers
+ * retrieve the timestamps from the other registers
* (starting from '1') with less complications.
*/
for (i = 1; i < IGC_MAX_TX_TSTAMP_REGS; i++) {
@@ -1008,7 +1049,7 @@ static int igc_phc_get_syncdevicetime(ktime_t *device,
*/
do {
/* Get a snapshot of system clocks to use as historic value. */
- ktime_get_snapshot(&adapter->snapshot);
+ ktime_get_snapshot_id(adapter->snapshot_clock_id, &adapter->snapshot);
igc_ptm_trigger(hw);
@@ -1062,6 +1103,8 @@ static int igc_ptp_getcrosststamp(struct ptp_clock_info *ptp,
/* This blocks until any in progress PTM transactions complete */
mutex_lock(&adapter->ptm_lock);
+ adapter->snapshot_clock_id = cts->clock_id;
+
ret = get_device_system_crosststamp(igc_phc_get_syncdevicetime,
adapter, &adapter->snapshot, cts);
mutex_unlock(&adapter->ptm_lock);
diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.c b/drivers/net/ethernet/intel/igc/igc_tsn.c
index 8a110145bfee..d23a45a34fa3 100644
--- a/drivers/net/ethernet/intel/igc/igc_tsn.c
+++ b/drivers/net/ethernet/intel/igc/igc_tsn.c
@@ -34,6 +34,7 @@ static int igc_fpe_init_smd_frame(struct igc_ring *ring,
return -ENOMEM;
}
+ buffer->type = IGC_TX_BUFFER_TYPE_SKB;
buffer->skb = skb;
buffer->protocol = 0;
buffer->bytecount = skb->len;
@@ -109,10 +110,16 @@ static int igc_fpe_xmit_smd_frame(struct igc_adapter *adapter,
__netif_tx_lock(nq, cpu);
err = igc_fpe_init_tx_descriptor(ring, skb, type);
- igc_flush_tx_descriptors(ring);
+ if (err)
+ goto err_free_skb_any;
+ igc_flush_tx_descriptors(ring);
__netif_tx_unlock(nq);
+ return 0;
+err_free_skb_any:
+ __netif_tx_unlock(nq);
+ dev_kfree_skb_any(skb);
return err;
}
@@ -175,14 +182,16 @@ static u32 igc_fpe_map_preempt_tc_to_queue(const struct igc_adapter *adapter,
struct net_device *dev = adapter->netdev;
u32 i, queue = 0;
- for (i = 0; i < dev->num_tc; i++) {
+ for (i = 0; i < netdev_get_num_tc(dev); i++) {
+ struct netdev_tc_txq res;
u32 offset, count;
if (!(preemptible_tcs & BIT(i)))
continue;
- offset = dev->tc_to_txq[i].offset;
- count = dev->tc_to_txq[i].count;
+ res.combined = READ_ONCE(dev->tc_to_txq[i].combined);
+ offset = res.offset;
+ count = res.count;
queue |= GENMASK(offset + count - 1, offset);
}