diff options
Diffstat (limited to 'drivers/net/ethernet/intel/igc')
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc.h | 13 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_defines.h | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_ethtool.c | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_main.c | 174 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_ptp.c | 25 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_tsn.c | 68 | 
6 files changed, 217 insertions, 67 deletions
diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h index 00a5ee487812..38901d2a4680 100644 --- a/drivers/net/ethernet/intel/igc/igc.h +++ b/drivers/net/ethernet/intel/igc/igc.h @@ -14,6 +14,7 @@  #include <linux/timecounter.h>  #include <linux/net_tstamp.h>  #include <linux/bitfield.h> +#include <linux/hrtimer.h>  #include "igc_hw.h" @@ -101,6 +102,8 @@ struct igc_ring {  	u32 start_time;  	u32 end_time;  	u32 max_sdu; +	bool oper_gate_closed;		/* Operating gate. True if the TX Queue is closed */ +	bool admin_gate_closed;		/* Future gate. True if the TX Queue will be closed */  	/* CBS parameters */  	bool cbs_enable;                /* indicates if CBS is enabled */ @@ -160,6 +163,7 @@ struct igc_adapter {  	struct timer_list watchdog_timer;  	struct timer_list dma_err_timer;  	struct timer_list phy_info_timer; +	struct hrtimer hrtimer;  	u32 wol;  	u32 en_mng_pt; @@ -184,10 +188,17 @@ struct igc_adapter {  	u32 max_frame_size;  	u32 min_frame_size; +	int tc_setup_type;  	ktime_t base_time;  	ktime_t cycle_time; -	bool qbv_enable; +	bool taprio_offload_enable;  	u32 qbv_config_change_errors; +	bool qbv_transition; +	unsigned int qbv_count; +	/* Access to oper_gate_closed, admin_gate_closed and qbv_transition +	 * are protected by the qbv_tx_lock. +	 */ +	spinlock_t qbv_tx_lock;  	/* OS defined structs */  	struct pci_dev *pdev; diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h index 44a507029946..2f780cc90883 100644 --- a/drivers/net/ethernet/intel/igc/igc_defines.h +++ b/drivers/net/ethernet/intel/igc/igc_defines.h @@ -546,7 +546,7 @@  #define IGC_PTM_CTRL_START_NOW	BIT(29) /* Start PTM Now */  #define IGC_PTM_CTRL_EN		BIT(30) /* Enable PTM */  #define IGC_PTM_CTRL_TRIG	BIT(31) /* PTM Cycle trigger */ -#define IGC_PTM_CTRL_SHRT_CYC(usec)	(((usec) & 0x2f) << 2) +#define IGC_PTM_CTRL_SHRT_CYC(usec)	(((usec) & 0x3f) << 2)  #define IGC_PTM_CTRL_PTM_TO(usec)	(((usec) & 0xff) << 8)  #define IGC_PTM_SHORT_CYC_DEFAULT	10  /* Default Short/interrupted cycle interval */ diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c index 0e2cb00622d1..93bce729be76 100644 --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c @@ -1708,6 +1708,8 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev,  	/* twisted pair */  	cmd->base.port = PORT_TP;  	cmd->base.phy_address = hw->phy.addr; +	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) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 019ce91c45aa..6f557e843e49 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -316,6 +316,33 @@ static void igc_clean_all_tx_rings(struct igc_adapter *adapter)  			igc_clean_tx_ring(adapter->tx_ring[i]);  } +static void igc_disable_tx_ring_hw(struct igc_ring *ring) +{ +	struct igc_hw *hw = &ring->q_vector->adapter->hw; +	u8 idx = ring->reg_idx; +	u32 txdctl; + +	txdctl = rd32(IGC_TXDCTL(idx)); +	txdctl &= ~IGC_TXDCTL_QUEUE_ENABLE; +	txdctl |= IGC_TXDCTL_SWFLUSH; +	wr32(IGC_TXDCTL(idx), txdctl); +} + +/** + * igc_disable_all_tx_rings_hw - Disable all transmit queue operation + * @adapter: board private structure + */ +static void igc_disable_all_tx_rings_hw(struct igc_adapter *adapter) +{ +	int i; + +	for (i = 0; i < adapter->num_tx_queues; i++) { +		struct igc_ring *tx_ring = adapter->tx_ring[i]; + +		igc_disable_tx_ring_hw(tx_ring); +	} +} +  /**   * igc_setup_tx_resources - allocate Tx resources (Descriptors)   * @tx_ring: tx descriptor ring (for a specific queue) to setup @@ -711,7 +738,6 @@ static void igc_configure_tx_ring(struct igc_adapter *adapter,  	/* disable the queue */  	wr32(IGC_TXDCTL(reg_idx), 0);  	wrfl(); -	mdelay(10);  	wr32(IGC_TDLEN(reg_idx),  	     ring->count * sizeof(union igc_adv_tx_desc)); @@ -1017,7 +1043,7 @@ static __le32 igc_tx_launchtime(struct igc_ring *ring, ktime_t txtime,  	ktime_t base_time = adapter->base_time;  	ktime_t now = ktime_get_clocktai();  	ktime_t baset_est, end_of_cycle; -	u32 launchtime; +	s32 launchtime;  	s64 n;  	n = div64_s64(ktime_sub_ns(now, base_time), cycle_time); @@ -1030,7 +1056,7 @@ static __le32 igc_tx_launchtime(struct igc_ring *ring, ktime_t txtime,  			*first_flag = true;  			ring->last_ff_cycle = baset_est; -			if (ktime_compare(txtime, ring->last_tx_cycle) > 0) +			if (ktime_compare(end_of_cycle, ring->last_tx_cycle) > 0)  				*insert_empty = true;  		}  	} @@ -1573,16 +1599,12 @@ done:  	first->bytecount = skb->len;  	first->gso_segs = 1; -	if (tx_ring->max_sdu > 0) { -		u32 max_sdu = 0; - -		max_sdu = tx_ring->max_sdu + -			  (skb_vlan_tagged(first->skb) ? VLAN_HLEN : 0); +	if (adapter->qbv_transition || tx_ring->oper_gate_closed) +		goto out_drop; -		if (first->bytecount > max_sdu) { -			adapter->stats.txdrop++; -			goto out_drop; -		} +	if (tx_ring->max_sdu > 0 && first->bytecount > tx_ring->max_sdu) { +		adapter->stats.txdrop++; +		goto out_drop;  	}  	if (unlikely(test_bit(IGC_RING_FLAG_TX_HWTSTAMP, &tx_ring->flags) && @@ -2833,9 +2855,8 @@ static void igc_xdp_xmit_zc(struct igc_ring *ring)  	struct netdev_queue *nq = txring_txq(ring);  	union igc_adv_tx_desc *tx_desc = NULL;  	int cpu = smp_processor_id(); -	u16 ntu = ring->next_to_use;  	struct xdp_desc xdp_desc; -	u16 budget; +	u16 budget, ntu;  	if (!netif_carrier_ok(ring->netdev))  		return; @@ -2845,6 +2866,7 @@ static void igc_xdp_xmit_zc(struct igc_ring *ring)  	/* Avoid transmit queue timeout since we share it with the slow path */  	txq_trans_cond_update(nq); +	ntu = ring->next_to_use;  	budget = igc_desc_unused(ring);  	while (xsk_tx_peek_desc(pool, &xdp_desc) && budget--) { @@ -3012,8 +3034,8 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget)  		    time_after(jiffies, tx_buffer->time_stamp +  		    (adapter->tx_timeout_factor * HZ)) &&  		    !(rd32(IGC_STATUS) & IGC_STATUS_TXOFF) && -		    (rd32(IGC_TDH(tx_ring->reg_idx)) != -		     readl(tx_ring->tail))) { +		    (rd32(IGC_TDH(tx_ring->reg_idx)) != readl(tx_ring->tail)) && +		    !tx_ring->oper_gate_closed) {  			/* detected Tx unit hang */  			netdev_err(tx_ring->netdev,  				   "Detected Tx Unit Hang\n" @@ -4779,6 +4801,7 @@ static int igc_sw_init(struct igc_adapter *adapter)  	adapter->nfc_rule_count = 0;  	spin_lock_init(&adapter->stats64_lock); +	spin_lock_init(&adapter->qbv_tx_lock);  	/* Assume MSI-X interrupts, will be checked during IRQ allocation */  	adapter->flags |= IGC_FLAG_HAS_MSIX; @@ -5063,6 +5086,7 @@ void igc_down(struct igc_adapter *adapter)  	/* clear VLAN promisc flag so VFTA will be updated if necessary */  	adapter->flags &= ~IGC_FLAG_VLAN_PROMISC; +	igc_disable_all_tx_rings_hw(adapter);  	igc_clean_all_tx_rings(adapter);  	igc_clean_all_rx_rings(adapter);  } @@ -6096,13 +6120,16 @@ static int igc_tsn_enable_launchtime(struct igc_adapter *adapter,  	return igc_tsn_offload_apply(adapter);  } -static int igc_tsn_clear_schedule(struct igc_adapter *adapter) +static int igc_qbv_clear_schedule(struct igc_adapter *adapter)  { +	unsigned long flags;  	int i;  	adapter->base_time = 0;  	adapter->cycle_time = NSEC_PER_SEC; +	adapter->taprio_offload_enable = false;  	adapter->qbv_config_change_errors = 0; +	adapter->qbv_count = 0;  	for (i = 0; i < adapter->num_tx_queues; i++) {  		struct igc_ring *ring = adapter->tx_ring[i]; @@ -6112,6 +6139,26 @@ static int igc_tsn_clear_schedule(struct igc_adapter *adapter)  		ring->max_sdu = 0;  	} +	spin_lock_irqsave(&adapter->qbv_tx_lock, flags); + +	adapter->qbv_transition = false; + +	for (i = 0; i < adapter->num_tx_queues; i++) { +		struct igc_ring *ring = adapter->tx_ring[i]; + +		ring->oper_gate_closed = false; +		ring->admin_gate_closed = false; +	} + +	spin_unlock_irqrestore(&adapter->qbv_tx_lock, flags); + +	return 0; +} + +static int igc_tsn_clear_schedule(struct igc_adapter *adapter) +{ +	igc_qbv_clear_schedule(adapter); +  	return 0;  } @@ -6121,27 +6168,21 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter,  	bool queue_configured[IGC_MAX_TX_QUEUES] = { };  	struct igc_hw *hw = &adapter->hw;  	u32 start_time = 0, end_time = 0; +	struct timespec64 now; +	unsigned long flags;  	size_t n;  	int i; -	switch (qopt->cmd) { -	case TAPRIO_CMD_REPLACE: -		adapter->qbv_enable = true; -		break; -	case TAPRIO_CMD_DESTROY: -		adapter->qbv_enable = false; -		break; -	default: -		return -EOPNOTSUPP; -	} - -	if (!adapter->qbv_enable) +	if (qopt->cmd == TAPRIO_CMD_DESTROY)  		return igc_tsn_clear_schedule(adapter); +	if (qopt->cmd != TAPRIO_CMD_REPLACE) +		return -EOPNOTSUPP; +  	if (qopt->base_time < 0)  		return -ERANGE; -	if (igc_is_device_id_i225(hw) && adapter->base_time) +	if (igc_is_device_id_i225(hw) && adapter->taprio_offload_enable)  		return -EALREADY;  	if (!validate_schedule(adapter, qopt)) @@ -6149,6 +6190,9 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter,  	adapter->cycle_time = qopt->cycle_time;  	adapter->base_time = qopt->base_time; +	adapter->taprio_offload_enable = true; + +	igc_ptp_read(adapter, &now);  	for (n = 0; n < qopt->num_entries; n++) {  		struct tc_taprio_sched_entry *e = &qopt->entries[n]; @@ -6184,30 +6228,49 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter,  				ring->start_time = start_time;  			ring->end_time = end_time; -			queue_configured[i] = true; +			if (ring->start_time >= adapter->cycle_time) +				queue_configured[i] = false; +			else +				queue_configured[i] = true;  		}  		start_time += e->interval;  	} +	spin_lock_irqsave(&adapter->qbv_tx_lock, flags); +  	/* Check whether a queue gets configured.  	 * If not, set the start and end time to be end time.  	 */  	for (i = 0; i < adapter->num_tx_queues; i++) { +		struct igc_ring *ring = adapter->tx_ring[i]; + +		if (!is_base_time_past(qopt->base_time, &now)) { +			ring->admin_gate_closed = false; +		} else { +			ring->oper_gate_closed = false; +			ring->admin_gate_closed = false; +		} +  		if (!queue_configured[i]) { -			struct igc_ring *ring = adapter->tx_ring[i]; +			if (!is_base_time_past(qopt->base_time, &now)) +				ring->admin_gate_closed = true; +			else +				ring->oper_gate_closed = true;  			ring->start_time = end_time;  			ring->end_time = end_time;  		}  	} +	spin_unlock_irqrestore(&adapter->qbv_tx_lock, flags); +  	for (i = 0; i < adapter->num_tx_queues; i++) {  		struct igc_ring *ring = adapter->tx_ring[i];  		struct net_device *dev = adapter->netdev;  		if (qopt->max_sdu[i]) -			ring->max_sdu = qopt->max_sdu[i] + dev->hard_header_len; +			ring->max_sdu = qopt->max_sdu[i] + dev->hard_header_len - ETH_TLEN;  		else  			ring->max_sdu = 0;  	} @@ -6327,6 +6390,8 @@ static int igc_setup_tc(struct net_device *dev, enum tc_setup_type type,  {  	struct igc_adapter *adapter = netdev_priv(dev); +	adapter->tc_setup_type = type; +  	switch (type) {  	case TC_QUERY_CAPS:  		return igc_tc_query_caps(adapter, type_data); @@ -6574,6 +6639,33 @@ static const struct xdp_metadata_ops igc_xdp_metadata_ops = {  	.xmo_rx_timestamp		= igc_xdp_rx_timestamp,  }; +static enum hrtimer_restart igc_qbv_scheduling_timer(struct hrtimer *timer) +{ +	struct igc_adapter *adapter = container_of(timer, struct igc_adapter, +						   hrtimer); +	unsigned long flags; +	unsigned int i; + +	spin_lock_irqsave(&adapter->qbv_tx_lock, flags); + +	adapter->qbv_transition = true; +	for (i = 0; i < adapter->num_tx_queues; i++) { +		struct igc_ring *tx_ring = adapter->tx_ring[i]; + +		if (tx_ring->admin_gate_closed) { +			tx_ring->admin_gate_closed = false; +			tx_ring->oper_gate_closed = true; +		} else { +			tx_ring->oper_gate_closed = false; +		} +	} +	adapter->qbv_transition = false; + +	spin_unlock_irqrestore(&adapter->qbv_tx_lock, flags); + +	return HRTIMER_NORESTART; +} +  /**   * igc_probe - Device Initialization Routine   * @pdev: PCI device information struct @@ -6752,6 +6844,9 @@ static int igc_probe(struct pci_dev *pdev,  	INIT_WORK(&adapter->reset_task, igc_reset_task);  	INIT_WORK(&adapter->watchdog_task, igc_watchdog_task); +	hrtimer_init(&adapter->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); +	adapter->hrtimer.function = &igc_qbv_scheduling_timer; +  	/* Initialize link properties that are user-changeable */  	adapter->fc_autoneg = true;  	hw->mac.autoneg = true; @@ -6855,6 +6950,7 @@ static void igc_remove(struct pci_dev *pdev)  	cancel_work_sync(&adapter->reset_task);  	cancel_work_sync(&adapter->watchdog_task); +	hrtimer_cancel(&adapter->hrtimer);  	/* Release control of h/w to f/w.  If f/w is AMT enabled, this  	 * would have already happened in close and is redundant. @@ -7252,18 +7348,6 @@ void igc_enable_rx_ring(struct igc_ring *ring)  		igc_alloc_rx_buffers(ring, igc_desc_unused(ring));  } -static void igc_disable_tx_ring_hw(struct igc_ring *ring) -{ -	struct igc_hw *hw = &ring->q_vector->adapter->hw; -	u8 idx = ring->reg_idx; -	u32 txdctl; - -	txdctl = rd32(IGC_TXDCTL(idx)); -	txdctl &= ~IGC_TXDCTL_QUEUE_ENABLE; -	txdctl |= IGC_TXDCTL_SWFLUSH; -	wr32(IGC_TXDCTL(idx), txdctl); -} -  void igc_disable_tx_ring(struct igc_ring *ring)  {  	igc_disable_tx_ring_hw(ring); diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c index 32ef112f8291..f0b979a70655 100644 --- a/drivers/net/ethernet/intel/igc/igc_ptp.c +++ b/drivers/net/ethernet/intel/igc/igc_ptp.c @@ -356,16 +356,35 @@ static int igc_ptp_feature_enable_i225(struct ptp_clock_info *ptp,  			tsim &= ~IGC_TSICR_TT0;  		}  		if (on) { +			struct timespec64 safe_start;  			int i = rq->perout.index;  			igc_pin_perout(igc, i, pin, use_freq); -			igc->perout[i].start.tv_sec = rq->perout.start.sec; +			igc_ptp_read(igc, &safe_start); + +			/* PPS output start time is triggered by Target time(TT) +			 * register. Programming any past time value into TT +			 * register will cause PPS to never start. Need to make +			 * sure we program the TT register a time ahead in +			 * future. There isn't a stringent need to fire PPS out +			 * right away. Adding +2 seconds should take care of +			 * corner cases. Let's say if the SYSTIML is close to +			 * wrap up and the timer keeps ticking as we program the +			 * register, adding +2seconds is safe bet. +			 */ +			safe_start.tv_sec += 2; + +			if (rq->perout.start.sec < safe_start.tv_sec) +				igc->perout[i].start.tv_sec = safe_start.tv_sec; +			else +				igc->perout[i].start.tv_sec = rq->perout.start.sec;  			igc->perout[i].start.tv_nsec = rq->perout.start.nsec;  			igc->perout[i].period.tv_sec = ts.tv_sec;  			igc->perout[i].period.tv_nsec = ts.tv_nsec; -			wr32(trgttimh, rq->perout.start.sec); +			wr32(trgttimh, (u32)igc->perout[i].start.tv_sec);  			/* For now, always select timer 0 as source. */ -			wr32(trgttiml, rq->perout.start.nsec | IGC_TT_IO_TIMER_SEL_SYSTIM0); +			wr32(trgttiml, (u32)(igc->perout[i].start.tv_nsec | +					     IGC_TT_IO_TIMER_SEL_SYSTIM0));  			if (use_freq)  				wr32(freqout, ns);  			tsauxc |= tsauxc_mask; diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.c b/drivers/net/ethernet/intel/igc/igc_tsn.c index 94a2b0dfb54d..a9c08321aca9 100644 --- a/drivers/net/ethernet/intel/igc/igc_tsn.c +++ b/drivers/net/ethernet/intel/igc/igc_tsn.c @@ -37,7 +37,7 @@ static unsigned int igc_tsn_new_flags(struct igc_adapter *adapter)  {  	unsigned int new_flags = adapter->flags & ~IGC_FLAG_TSN_ANY_ENABLED; -	if (adapter->qbv_enable) +	if (adapter->taprio_offload_enable)  		new_flags |= IGC_FLAG_TSN_QBV_ENABLED;  	if (is_any_launchtime(adapter)) @@ -114,7 +114,6 @@ static int igc_tsn_disable_offload(struct igc_adapter *adapter)  static int igc_tsn_enable_offload(struct igc_adapter *adapter)  {  	struct igc_hw *hw = &adapter->hw; -	bool tsn_mode_reconfig = false;  	u32 tqavctrl, baset_l, baset_h;  	u32 sec, nsec, cycle;  	ktime_t base_time, systim; @@ -133,8 +132,28 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter)  		wr32(IGC_STQT(i), ring->start_time);  		wr32(IGC_ENDQT(i), ring->end_time); -		txqctl |= IGC_TXQCTL_STRICT_CYCLE | -			IGC_TXQCTL_STRICT_END; +		if (adapter->taprio_offload_enable) { +			/* If taprio_offload_enable is set we are in "taprio" +			 * mode and we need to be strict about the +			 * cycles: only transmit a packet if it can be +			 * completed during that cycle. +			 * +			 * If taprio_offload_enable is NOT true when +			 * enabling TSN offload, the cycle should have +			 * no external effects, but is only used internally +			 * to adapt the base time register after a second +			 * has passed. +			 * +			 * Enabling strict mode in this case would +			 * unnecessarily prevent the transmission of +			 * certain packets (i.e. at the boundary of a +			 * second) and thus interfere with the launchtime +			 * feature that promises transmission at a +			 * certain point in time. +			 */ +			txqctl |= IGC_TXQCTL_STRICT_CYCLE | +				IGC_TXQCTL_STRICT_END; +		}  		if (ring->launchtime_enable)  			txqctl |= IGC_TXQCTL_QUEUE_MODE_LAUNCHT; @@ -228,11 +247,10 @@ skip_cbs:  	tqavctrl = rd32(IGC_TQAVCTRL) & ~IGC_TQAVCTRL_FUTSCDDIS; -	if (tqavctrl & IGC_TQAVCTRL_TRANSMIT_MODE_TSN) -		tsn_mode_reconfig = true; -  	tqavctrl |= IGC_TQAVCTRL_TRANSMIT_MODE_TSN | IGC_TQAVCTRL_ENHANCED_QAV; +	adapter->qbv_count++; +  	cycle = adapter->cycle_time;  	base_time = adapter->base_time; @@ -249,17 +267,29 @@ skip_cbs:  		 * Gate Control List (GCL) is running.  		 */  		if ((rd32(IGC_BASET_H) || rd32(IGC_BASET_L)) && -		    tsn_mode_reconfig) +		    (adapter->tc_setup_type == TC_SETUP_QDISC_TAPRIO) && +		    (adapter->qbv_count > 1))  			adapter->qbv_config_change_errors++;  	} else { -		/* According to datasheet section 7.5.2.9.3.3, FutScdDis bit -		 * has to be configured before the cycle time and base time. -		 * Tx won't hang if there is a GCL is already running, -		 * so in this case we don't need to set FutScdDis. -		 */ -		if (igc_is_device_id_i226(hw) && -		    !(rd32(IGC_BASET_H) || rd32(IGC_BASET_L))) -			tqavctrl |= IGC_TQAVCTRL_FUTSCDDIS; +		if (igc_is_device_id_i226(hw)) { +			ktime_t adjust_time, expires_time; + +		       /* According to datasheet section 7.5.2.9.3.3, FutScdDis bit +			* has to be configured before the cycle time and base time. +			* Tx won't hang if a GCL is already running, +			* so in this case we don't need to set FutScdDis. +			*/ +			if (!(rd32(IGC_BASET_H) || rd32(IGC_BASET_L))) +				tqavctrl |= IGC_TQAVCTRL_FUTSCDDIS; + +			nsec = rd32(IGC_SYSTIML); +			sec = rd32(IGC_SYSTIMH); +			systim = ktime_set(sec, nsec); + +			adjust_time = adapter->base_time; +			expires_time = ktime_sub_ns(adjust_time, systim); +			hrtimer_start(&adapter->hrtimer, expires_time, HRTIMER_MODE_REL); +		}  	}  	wr32(IGC_TQAVCTRL, tqavctrl); @@ -305,7 +335,11 @@ int igc_tsn_offload_apply(struct igc_adapter *adapter)  {  	struct igc_hw *hw = &adapter->hw; -	if (netif_running(adapter->netdev) && igc_is_device_id_i225(hw)) { +	/* Per I225/6 HW Design Section 7.5.2.1, transmit mode +	 * cannot be changed dynamically. Require reset the adapter. +	 */ +	if (netif_running(adapter->netdev) && +	    (igc_is_device_id_i225(hw) || !adapter->qbv_count)) {  		schedule_work(&adapter->reset_task);  		return 0;  	}  | 
