diff options
Diffstat (limited to 'drivers/net/ethernet/intel/igc')
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc.h | 3 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_base.c | 29 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_base.h | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_defines.h | 14 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_ethtool.c | 12 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_main.c | 289 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_ptp.c | 24 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_regs.h | 1 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_tsn.c | 99 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_tsn.h | 1 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_xdp.c | 5 | 
11 files changed, 390 insertions, 89 deletions
diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h index 1e7e7071f64d..df3e26c0cf01 100644 --- a/drivers/net/ethernet/intel/igc/igc.h +++ b/drivers/net/ethernet/intel/igc/igc.h @@ -94,6 +94,8 @@ struct igc_ring {  	u8 queue_index;                 /* logical index of the ring*/  	u8 reg_idx;                     /* physical index of the ring */  	bool launchtime_enable;         /* true if LaunchTime is enabled */ +	ktime_t last_tx_cycle;          /* end of the cycle with a launchtime transmission */ +	ktime_t last_ff_cycle;          /* Last cycle with an active first flag */  	u32 start_time;  	u32 end_time; @@ -182,6 +184,7 @@ struct igc_adapter {  	ktime_t base_time;  	ktime_t cycle_time; +	bool qbv_enable;  	/* OS defined structs */  	struct pci_dev *pdev; diff --git a/drivers/net/ethernet/intel/igc/igc_base.c b/drivers/net/ethernet/intel/igc/igc_base.c index a15927e77272..a1d815af507d 100644 --- a/drivers/net/ethernet/intel/igc/igc_base.c +++ b/drivers/net/ethernet/intel/igc/igc_base.c @@ -396,6 +396,35 @@ void igc_rx_fifo_flush_base(struct igc_hw *hw)  	rd32(IGC_MPC);  } +bool igc_is_device_id_i225(struct igc_hw *hw) +{ +	switch (hw->device_id) { +	case IGC_DEV_ID_I225_LM: +	case IGC_DEV_ID_I225_V: +	case IGC_DEV_ID_I225_I: +	case IGC_DEV_ID_I225_K: +	case IGC_DEV_ID_I225_K2: +	case IGC_DEV_ID_I225_LMVP: +	case IGC_DEV_ID_I225_IT: +		return true; +	default: +		return false; +	} +} + +bool igc_is_device_id_i226(struct igc_hw *hw) +{ +	switch (hw->device_id) { +	case IGC_DEV_ID_I226_LM: +	case IGC_DEV_ID_I226_V: +	case IGC_DEV_ID_I226_K: +	case IGC_DEV_ID_I226_IT: +		return true; +	default: +		return false; +	} +} +  static struct igc_mac_operations igc_mac_ops_base = {  	.init_hw		= igc_init_hw_base,  	.check_for_link		= igc_check_for_copper_link, diff --git a/drivers/net/ethernet/intel/igc/igc_base.h b/drivers/net/ethernet/intel/igc/igc_base.h index ce530f5fd7bd..7a992befca24 100644 --- a/drivers/net/ethernet/intel/igc/igc_base.h +++ b/drivers/net/ethernet/intel/igc/igc_base.h @@ -7,6 +7,8 @@  /* forward declaration */  void igc_rx_fifo_flush_base(struct igc_hw *hw);  void igc_power_down_phy_copper_base(struct igc_hw *hw); +bool igc_is_device_id_i225(struct igc_hw *hw); +bool igc_is_device_id_i226(struct igc_hw *hw);  /* Transmit Descriptor - Advanced */  union igc_adv_tx_desc { diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h index 4f9d7f013a95..9dec3563ce3a 100644 --- a/drivers/net/ethernet/intel/igc/igc_defines.h +++ b/drivers/net/ethernet/intel/igc/igc_defines.h @@ -321,6 +321,8 @@  #define IGC_ADVTXD_L4LEN_SHIFT	8  /* Adv ctxt L4LEN shift */  #define IGC_ADVTXD_MSS_SHIFT	16 /* Adv ctxt MSS shift */ +#define IGC_ADVTXD_TSN_CNTX_FIRST	0x00000080 +  /* Transmit Control */  #define IGC_TCTL_EN		0x00000002 /* enable Tx */  #define IGC_TCTL_PSP		0x00000008 /* pad short packets */ @@ -400,6 +402,15 @@  #define IGC_DTXMXPKTSZ_TSN	0x19 /* 1600 bytes of max TX DMA packet size */  #define IGC_DTXMXPKTSZ_DEFAULT	0x98 /* 9728-byte Jumbo frames */ +/* Transmit Scheduling Latency */ +/* Latency between transmission scheduling (LaunchTime) and the time + * the packet is transmitted to the network in nanosecond. + */ +#define IGC_TXOFFSET_SPEED_10	0x000034BC +#define IGC_TXOFFSET_SPEED_100	0x00000578 +#define IGC_TXOFFSET_SPEED_1000	0x0000012C +#define IGC_TXOFFSET_SPEED_2500	0x00000578 +  /* Time Sync Interrupt Causes */  #define IGC_TSICR_SYS_WRAP	BIT(0) /* SYSTIM Wrap around. */  #define IGC_TSICR_TXTS		BIT(1) /* Transmit Timestamp. */ @@ -464,7 +475,9 @@  #define IGC_TSAUXC_EN_TT0	BIT(0)  /* Enable target time 0. */  #define IGC_TSAUXC_EN_TT1	BIT(1)  /* Enable target time 1. */  #define IGC_TSAUXC_EN_CLK0	BIT(2)  /* Enable Configurable Frequency Clock 0. */ +#define IGC_TSAUXC_ST0		BIT(4)  /* Start Clock 0 Toggle on Target Time 0. */  #define IGC_TSAUXC_EN_CLK1	BIT(5)  /* Enable Configurable Frequency Clock 1. */ +#define IGC_TSAUXC_ST1		BIT(7)  /* Start Clock 1 Toggle on Target Time 1. */  #define IGC_TSAUXC_EN_TS0	BIT(8)  /* Enable hardware timestamp 0. */  #define IGC_TSAUXC_AUTT0	BIT(9)  /* Auxiliary Timestamp Taken. */  #define IGC_TSAUXC_EN_TS1	BIT(10) /* Enable hardware timestamp 0. */ @@ -511,6 +524,7 @@  /* Transmit Scheduling */  #define IGC_TQAVCTRL_TRANSMIT_MODE_TSN	0x00000001  #define IGC_TQAVCTRL_ENHANCED_QAV	0x00000008 +#define IGC_TQAVCTRL_FUTSCDDIS		0x00000080  #define IGC_TXQCTL_QUEUE_MODE_LAUNCHT	0x00000001  #define IGC_TXQCTL_STRICT_CYCLE		0x00000002 diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c index 8cc077b712ad..5a26a7805ef8 100644 --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c @@ -839,15 +839,15 @@ static void igc_ethtool_get_stats(struct net_device *netdev,  		ring = adapter->tx_ring[j];  		do { -			start = u64_stats_fetch_begin_irq(&ring->tx_syncp); +			start = u64_stats_fetch_begin(&ring->tx_syncp);  			data[i]   = ring->tx_stats.packets;  			data[i + 1] = ring->tx_stats.bytes;  			data[i + 2] = ring->tx_stats.restart_queue; -		} while (u64_stats_fetch_retry_irq(&ring->tx_syncp, start)); +		} while (u64_stats_fetch_retry(&ring->tx_syncp, start));  		do { -			start = u64_stats_fetch_begin_irq(&ring->tx_syncp2); +			start = u64_stats_fetch_begin(&ring->tx_syncp2);  			restart2  = ring->tx_stats.restart_queue2; -		} while (u64_stats_fetch_retry_irq(&ring->tx_syncp2, start)); +		} while (u64_stats_fetch_retry(&ring->tx_syncp2, start));  		data[i + 2] += restart2;  		i += IGC_TX_QUEUE_STATS_LEN; @@ -855,13 +855,13 @@ static void igc_ethtool_get_stats(struct net_device *netdev,  	for (j = 0; j < adapter->num_rx_queues; j++) {  		ring = adapter->rx_ring[j];  		do { -			start = u64_stats_fetch_begin_irq(&ring->rx_syncp); +			start = u64_stats_fetch_begin(&ring->rx_syncp);  			data[i]   = ring->rx_stats.packets;  			data[i + 1] = ring->rx_stats.bytes;  			data[i + 2] = ring->rx_stats.drops;  			data[i + 3] = ring->rx_stats.csum_err;  			data[i + 4] = ring->rx_stats.alloc_failed; -		} while (u64_stats_fetch_retry_irq(&ring->rx_syncp, start)); +		} while (u64_stats_fetch_retry(&ring->rx_syncp, start));  		i += IGC_RX_QUEUE_STATS_LEN;  	}  	spin_unlock(&adapter->stats64_lock); diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 34889be63e78..2928a6c73692 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -1000,25 +1000,118 @@ static int igc_write_mc_addr_list(struct net_device *netdev)  	return netdev_mc_count(netdev);  } -static __le32 igc_tx_launchtime(struct igc_adapter *adapter, ktime_t txtime) +static __le32 igc_tx_launchtime(struct igc_ring *ring, ktime_t txtime, +				bool *first_flag, bool *insert_empty)  { +	struct igc_adapter *adapter = netdev_priv(ring->netdev);  	ktime_t cycle_time = adapter->cycle_time;  	ktime_t base_time = adapter->base_time; +	ktime_t now = ktime_get_clocktai(); +	ktime_t baset_est, end_of_cycle;  	u32 launchtime; +	s64 n; -	/* FIXME: when using ETF together with taprio, we may have a -	 * case where 'delta' is larger than the cycle_time, this may -	 * cause problems if we don't read the current value of -	 * IGC_BASET, as the value writen into the launchtime -	 * descriptor field may be misinterpreted. +	n = div64_s64(ktime_sub_ns(now, base_time), cycle_time); + +	baset_est = ktime_add_ns(base_time, cycle_time * (n)); +	end_of_cycle = ktime_add_ns(baset_est, cycle_time); + +	if (ktime_compare(txtime, end_of_cycle) >= 0) { +		if (baset_est != ring->last_ff_cycle) { +			*first_flag = true; +			ring->last_ff_cycle = baset_est; + +			if (ktime_compare(txtime, ring->last_tx_cycle) > 0) +				*insert_empty = true; +		} +	} + +	/* Introducing a window at end of cycle on which packets +	 * potentially not honor launchtime. Window of 5us chosen +	 * considering software update the tail pointer and packets +	 * are dma'ed to packet buffer.  	 */ -	div_s64_rem(ktime_sub_ns(txtime, base_time), cycle_time, &launchtime); +	if ((ktime_sub_ns(end_of_cycle, now) < 5 * NSEC_PER_USEC)) +		netdev_warn(ring->netdev, "Packet with txtime=%llu may not be honoured\n", +			    txtime); + +	ring->last_tx_cycle = end_of_cycle; + +	launchtime = ktime_sub_ns(txtime, baset_est); +	if (launchtime > 0) +		div_s64_rem(launchtime, cycle_time, &launchtime); +	else +		launchtime = 0;  	return cpu_to_le32(launchtime);  } +static int igc_init_empty_frame(struct igc_ring *ring, +				struct igc_tx_buffer *buffer, +				struct sk_buff *skb) +{ +	unsigned int size; +	dma_addr_t dma; + +	size = skb_headlen(skb); + +	dma = dma_map_single(ring->dev, skb->data, size, DMA_TO_DEVICE); +	if (dma_mapping_error(ring->dev, dma)) { +		netdev_err_once(ring->netdev, "Failed to map DMA for TX\n"); +		return -ENOMEM; +	} + +	buffer->skb = skb; +	buffer->protocol = 0; +	buffer->bytecount = skb->len; +	buffer->gso_segs = 1; +	buffer->time_stamp = jiffies; +	dma_unmap_len_set(buffer, len, skb->len); +	dma_unmap_addr_set(buffer, dma, dma); + +	return 0; +} + +static int igc_init_tx_empty_descriptor(struct igc_ring *ring, +					struct sk_buff *skb, +					struct igc_tx_buffer *first) +{ +	union igc_adv_tx_desc *desc; +	u32 cmd_type, olinfo_status; +	int err; + +	if (!igc_desc_unused(ring)) +		return -EBUSY; + +	err = igc_init_empty_frame(ring, first, skb); +	if (err) +		return err; + +	cmd_type = IGC_ADVTXD_DTYP_DATA | IGC_ADVTXD_DCMD_DEXT | +		   IGC_ADVTXD_DCMD_IFCS | IGC_TXD_DCMD | +		   first->bytecount; +	olinfo_status = first->bytecount << IGC_ADVTXD_PAYLEN_SHIFT; + +	desc = IGC_TX_DESC(ring, ring->next_to_use); +	desc->read.cmd_type_len = cpu_to_le32(cmd_type); +	desc->read.olinfo_status = cpu_to_le32(olinfo_status); +	desc->read.buffer_addr = cpu_to_le64(dma_unmap_addr(first, dma)); + +	netdev_tx_sent_queue(txring_txq(ring), skb->len); + +	first->next_to_watch = desc; + +	ring->next_to_use++; +	if (ring->next_to_use == ring->count) +		ring->next_to_use = 0; + +	return 0; +} + +#define IGC_EMPTY_FRAME_SIZE 60 +  static void igc_tx_ctxtdesc(struct igc_ring *tx_ring, -			    struct igc_tx_buffer *first, +			    __le32 launch_time, bool first_flag,  			    u32 vlan_macip_lens, u32 type_tucmd,  			    u32 mss_l4len_idx)  { @@ -1037,26 +1130,17 @@ static void igc_tx_ctxtdesc(struct igc_ring *tx_ring,  	if (test_bit(IGC_RING_FLAG_TX_CTX_IDX, &tx_ring->flags))  		mss_l4len_idx |= tx_ring->reg_idx << 4; +	if (first_flag) +		mss_l4len_idx |= IGC_ADVTXD_TSN_CNTX_FIRST; +  	context_desc->vlan_macip_lens	= cpu_to_le32(vlan_macip_lens);  	context_desc->type_tucmd_mlhl	= cpu_to_le32(type_tucmd);  	context_desc->mss_l4len_idx	= cpu_to_le32(mss_l4len_idx); - -	/* We assume there is always a valid Tx time available. Invalid times -	 * should have been handled by the upper layers. -	 */ -	if (tx_ring->launchtime_enable) { -		struct igc_adapter *adapter = netdev_priv(tx_ring->netdev); -		ktime_t txtime = first->skb->tstamp; - -		skb_txtime_consumed(first->skb); -		context_desc->launch_time = igc_tx_launchtime(adapter, -							      txtime); -	} else { -		context_desc->launch_time = 0; -	} +	context_desc->launch_time	= launch_time;  } -static void igc_tx_csum(struct igc_ring *tx_ring, struct igc_tx_buffer *first) +static void igc_tx_csum(struct igc_ring *tx_ring, struct igc_tx_buffer *first, +			__le32 launch_time, bool first_flag)  {  	struct sk_buff *skb = first->skb;  	u32 vlan_macip_lens = 0; @@ -1096,7 +1180,8 @@ no_csum:  	vlan_macip_lens |= skb_network_offset(skb) << IGC_ADVTXD_MACLEN_SHIFT;  	vlan_macip_lens |= first->tx_flags & IGC_TX_FLAGS_VLAN_MASK; -	igc_tx_ctxtdesc(tx_ring, first, vlan_macip_lens, type_tucmd, 0); +	igc_tx_ctxtdesc(tx_ring, launch_time, first_flag, +			vlan_macip_lens, type_tucmd, 0);  }  static int __igc_maybe_stop_tx(struct igc_ring *tx_ring, const u16 size) @@ -1320,6 +1405,7 @@ dma_error:  static int igc_tso(struct igc_ring *tx_ring,  		   struct igc_tx_buffer *first, +		   __le32 launch_time, bool first_flag,  		   u8 *hdr_len)  {  	u32 vlan_macip_lens, type_tucmd, mss_l4len_idx; @@ -1406,8 +1492,8 @@ static int igc_tso(struct igc_ring *tx_ring,  	vlan_macip_lens |= (ip.hdr - skb->data) << IGC_ADVTXD_MACLEN_SHIFT;  	vlan_macip_lens |= first->tx_flags & IGC_TX_FLAGS_VLAN_MASK; -	igc_tx_ctxtdesc(tx_ring, first, vlan_macip_lens, -			type_tucmd, mss_l4len_idx); +	igc_tx_ctxtdesc(tx_ring, launch_time, first_flag, +			vlan_macip_lens, type_tucmd, mss_l4len_idx);  	return 1;  } @@ -1415,11 +1501,14 @@ static int igc_tso(struct igc_ring *tx_ring,  static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb,  				       struct igc_ring *tx_ring)  { +	bool first_flag = false, insert_empty = false;  	u16 count = TXD_USE_COUNT(skb_headlen(skb));  	__be16 protocol = vlan_get_protocol(skb);  	struct igc_tx_buffer *first; +	__le32 launch_time = 0;  	u32 tx_flags = 0;  	unsigned short f; +	ktime_t txtime;  	u8 hdr_len = 0;  	int tso = 0; @@ -1433,11 +1522,40 @@ static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb,  		count += TXD_USE_COUNT(skb_frag_size(  						&skb_shinfo(skb)->frags[f])); -	if (igc_maybe_stop_tx(tx_ring, count + 3)) { +	if (igc_maybe_stop_tx(tx_ring, count + 5)) {  		/* this is a hard error */  		return NETDEV_TX_BUSY;  	} +	if (!tx_ring->launchtime_enable) +		goto done; + +	txtime = skb->tstamp; +	skb->tstamp = ktime_set(0, 0); +	launch_time = igc_tx_launchtime(tx_ring, txtime, &first_flag, &insert_empty); + +	if (insert_empty) { +		struct igc_tx_buffer *empty_info; +		struct sk_buff *empty; +		void *data; + +		empty_info = &tx_ring->tx_buffer_info[tx_ring->next_to_use]; +		empty = alloc_skb(IGC_EMPTY_FRAME_SIZE, GFP_ATOMIC); +		if (!empty) +			goto done; + +		data = skb_put(empty, IGC_EMPTY_FRAME_SIZE); +		memset(data, 0, IGC_EMPTY_FRAME_SIZE); + +		igc_tx_ctxtdesc(tx_ring, 0, false, 0, 0, 0); + +		if (igc_init_tx_empty_descriptor(tx_ring, +						 empty, +						 empty_info) < 0) +			dev_kfree_skb_any(empty); +	} + +done:  	/* record the location of the first descriptor for this packet */  	first = &tx_ring->tx_buffer_info[tx_ring->next_to_use];  	first->type = IGC_TX_BUFFER_TYPE_SKB; @@ -1474,11 +1592,11 @@ static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb,  	first->tx_flags = tx_flags;  	first->protocol = protocol; -	tso = igc_tso(tx_ring, first, &hdr_len); +	tso = igc_tso(tx_ring, first, launch_time, first_flag, &hdr_len);  	if (tso < 0)  		goto out_drop;  	else if (!tso) -		igc_tx_csum(tx_ring, first); +		igc_tx_csum(tx_ring, first, launch_time, first_flag);  	igc_tx_map(tx_ring, first, hdr_len); @@ -2824,7 +2942,9 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget)  		if (tx_buffer->next_to_watch &&  		    time_after(jiffies, tx_buffer->time_stamp +  		    (adapter->tx_timeout_factor * HZ)) && -		    !(rd32(IGC_STATUS) & IGC_STATUS_TXOFF)) { +		    !(rd32(IGC_STATUS) & IGC_STATUS_TXOFF) && +		    (rd32(IGC_TDH(tx_ring->reg_idx)) != +		     readl(tx_ring->tail))) {  			/* detected Tx unit hang */  			netdev_err(tx_ring->netdev,  				   "Detected Tx Unit Hang\n" @@ -4682,10 +4802,10 @@ void igc_update_stats(struct igc_adapter *adapter)  		}  		do { -			start = u64_stats_fetch_begin_irq(&ring->rx_syncp); +			start = u64_stats_fetch_begin(&ring->rx_syncp);  			_bytes = ring->rx_stats.bytes;  			_packets = ring->rx_stats.packets; -		} while (u64_stats_fetch_retry_irq(&ring->rx_syncp, start)); +		} while (u64_stats_fetch_retry(&ring->rx_syncp, start));  		bytes += _bytes;  		packets += _packets;  	} @@ -4699,10 +4819,10 @@ void igc_update_stats(struct igc_adapter *adapter)  		struct igc_ring *ring = adapter->tx_ring[i];  		do { -			start = u64_stats_fetch_begin_irq(&ring->tx_syncp); +			start = u64_stats_fetch_begin(&ring->tx_syncp);  			_bytes = ring->tx_stats.bytes;  			_packets = ring->tx_stats.packets; -		} while (u64_stats_fetch_retry_irq(&ring->tx_syncp, start)); +		} while (u64_stats_fetch_retry(&ring->tx_syncp, start));  		bytes += _bytes;  		packets += _packets;  	} @@ -4951,6 +5071,24 @@ static int igc_change_mtu(struct net_device *netdev, int new_mtu)  }  /** + * igc_tx_timeout - Respond to a Tx Hang + * @netdev: network interface device structure + * @txqueue: queue number that timed out + **/ +static void igc_tx_timeout(struct net_device *netdev, +			   unsigned int __always_unused txqueue) +{ +	struct igc_adapter *adapter = netdev_priv(netdev); +	struct igc_hw *hw = &adapter->hw; + +	/* Do the reset outside of interrupt context */ +	adapter->tx_timeout_count++; +	schedule_work(&adapter->reset_task); +	wr32(IGC_EICS, +	     (adapter->eims_enable_mask & ~adapter->eims_other)); +} + +/**   * igc_get_stats64 - Get System Network Statistics   * @netdev: network interface device structure   * @stats: rtnl_link_stats64 pointer @@ -5377,10 +5515,17 @@ static void igc_watchdog_task(struct work_struct *work)  			case SPEED_100:  			case SPEED_1000:  			case SPEED_2500: -				adapter->tx_timeout_factor = 7; +				adapter->tx_timeout_factor = 1;  				break;  			} +			/* Once the launch time has been set on the wire, there +			 * is a delay before the link speed can be determined +			 * based on link-up activity. Write into the register +			 * as soon as we know the correct link speed. +			 */ +			igc_tsn_adjust_txtime_offset(adapter); +  			if (adapter->link_speed != SPEED_1000)  				goto no_wait; @@ -5833,6 +5978,7 @@ static bool validate_schedule(struct igc_adapter *adapter,  			      const struct tc_taprio_qopt_offload *qopt)  {  	int queue_uses[IGC_MAX_TX_QUEUES] = { }; +	struct igc_hw *hw = &adapter->hw;  	struct timespec64 now;  	size_t n; @@ -5845,8 +5991,10 @@ static bool validate_schedule(struct igc_adapter *adapter,  	 * in the future, it will hold all the packets until that  	 * time, causing a lot of TX Hangs, so to avoid that, we  	 * reject schedules that would start in the future. +	 * Note: Limitation above is no longer in i226.  	 */ -	if (!is_base_time_past(qopt->base_time, &now)) +	if (!is_base_time_past(qopt->base_time, &now) && +	    igc_is_device_id_i225(hw))  		return false;  	for (n = 0; n < qopt->num_entries; n++) { @@ -5916,13 +6064,20 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter,  				 struct tc_taprio_qopt_offload *qopt)  {  	bool queue_configured[IGC_MAX_TX_QUEUES] = { }; +	struct igc_hw *hw = &adapter->hw;  	u32 start_time = 0, end_time = 0;  	size_t n; +	int i; + +	adapter->qbv_enable = qopt->enable;  	if (!qopt->enable)  		return igc_tsn_clear_schedule(adapter); -	if (adapter->base_time) +	if (qopt->base_time < 0) +		return -ERANGE; + +	if (igc_is_device_id_i225(hw) && adapter->base_time)  		return -EALREADY;  	if (!validate_schedule(adapter, qopt)) @@ -5933,10 +6088,24 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter,  	for (n = 0; n < qopt->num_entries; n++) {  		struct tc_taprio_sched_entry *e = &qopt->entries[n]; -		int i;  		end_time += e->interval; +		/* If any of the conditions below are true, we need to manually +		 * control the end time of the cycle. +		 * 1. Qbv users can specify a cycle time that is not equal +		 * to the total GCL intervals. Hence, recalculation is +		 * necessary here to exclude the time interval that +		 * exceeds the cycle time. +		 * 2. According to IEEE Std. 802.1Q-2018 section 8.6.9.2, +		 * once the end of the list is reached, it will switch +		 * to the END_OF_CYCLE state and leave the gates in the +		 * same state until the next cycle is started. +		 */ +		if (end_time > adapter->cycle_time || +		    n + 1 == qopt->num_entries) +			end_time = adapter->cycle_time; +  		for (i = 0; i < adapter->num_tx_queues; i++) {  			struct igc_ring *ring = adapter->tx_ring[i]; @@ -5957,6 +6126,18 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter,  		start_time += e->interval;  	} +	/* 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++) { +		if (!queue_configured[i]) { +			struct igc_ring *ring = adapter->tx_ring[i]; + +			ring->start_time = end_time; +			ring->end_time = end_time; +		} +	} +  	return 0;  } @@ -6044,12 +6225,35 @@ static int igc_tsn_enable_cbs(struct igc_adapter *adapter,  	return igc_tsn_offload_apply(adapter);  } +static int igc_tc_query_caps(struct igc_adapter *adapter, +			     struct tc_query_caps_base *base) +{ +	struct igc_hw *hw = &adapter->hw; + +	switch (base->type) { +	case TC_SETUP_QDISC_TAPRIO: { +		struct tc_taprio_caps *caps = base->caps; + +		caps->broken_mqprio = true; + +		if (hw->mac.type == igc_i225) +			caps->gate_mask_per_txq = true; + +		return 0; +	} +	default: +		return -EOPNOTSUPP; +	} +} +  static int igc_setup_tc(struct net_device *dev, enum tc_setup_type type,  			void *type_data)  {  	struct igc_adapter *adapter = netdev_priv(dev);  	switch (type) { +	case TC_QUERY_CAPS: +		return igc_tc_query_caps(adapter, type_data);  	case TC_SETUP_QDISC_TAPRIO:  		return igc_tsn_enable_qbv_scheduling(adapter, type_data); @@ -6163,6 +6367,7 @@ static const struct net_device_ops igc_netdev_ops = {  	.ndo_set_rx_mode	= igc_set_rx_mode,  	.ndo_set_mac_address	= igc_set_mac,  	.ndo_change_mtu		= igc_change_mtu, +	.ndo_tx_timeout		= igc_tx_timeout,  	.ndo_get_stats64	= igc_get_stats64,  	.ndo_fix_features	= igc_fix_features,  	.ndo_set_features	= igc_set_features, @@ -6273,8 +6478,6 @@ static int igc_probe(struct pci_dev *pdev,  	if (err)  		goto err_pci_reg; -	pci_enable_pcie_error_reporting(pdev); -  	err = pci_enable_ptm(pdev, NULL);  	if (err < 0)  		dev_info(&pdev->dev, "PCIe PTM not supported by PCIe bus/controller\n"); @@ -6372,6 +6575,9 @@ static int igc_probe(struct pci_dev *pdev,  	netdev->mpls_features |= NETIF_F_HW_CSUM;  	netdev->hw_enc_features |= netdev->vlan_features; +	netdev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT | +			       NETDEV_XDP_ACT_XSK_ZEROCOPY; +  	/* MTU range: 68 - 9216 */  	netdev->min_mtu = ETH_MIN_MTU;  	netdev->max_mtu = MAX_STD_JUMBO_FRAME_SIZE; @@ -6479,7 +6685,6 @@ err_sw_init:  err_ioremap:  	free_netdev(netdev);  err_alloc_etherdev: -	pci_disable_pcie_error_reporting(pdev);  	pci_release_mem_regions(pdev);  err_pci_reg:  err_dma: @@ -6527,8 +6732,6 @@ static void igc_remove(struct pci_dev *pdev)  	free_netdev(netdev); -	pci_disable_pcie_error_reporting(pdev); -  	pci_disable_device(pdev);  } diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c index 8dbb9f903ca7..4e10ced736db 100644 --- a/drivers/net/ethernet/intel/igc/igc_ptp.c +++ b/drivers/net/ethernet/intel/igc/igc_ptp.c @@ -322,7 +322,7 @@ static int igc_ptp_feature_enable_i225(struct ptp_clock_info *ptp,  		ts = ns_to_timespec64(ns);  		if (rq->perout.index == 1) {  			if (use_freq) { -				tsauxc_mask = IGC_TSAUXC_EN_CLK1; +				tsauxc_mask = IGC_TSAUXC_EN_CLK1 | IGC_TSAUXC_ST1;  				tsim_mask = 0;  			} else {  				tsauxc_mask = IGC_TSAUXC_EN_TT1; @@ -333,7 +333,7 @@ static int igc_ptp_feature_enable_i225(struct ptp_clock_info *ptp,  			freqout = IGC_FREQOUT1;  		} else {  			if (use_freq) { -				tsauxc_mask = IGC_TSAUXC_EN_CLK0; +				tsauxc_mask = IGC_TSAUXC_EN_CLK0 | IGC_TSAUXC_ST0;  				tsim_mask = 0;  			} else {  				tsauxc_mask = IGC_TSAUXC_EN_TT0; @@ -347,10 +347,12 @@ static int igc_ptp_feature_enable_i225(struct ptp_clock_info *ptp,  		tsauxc = rd32(IGC_TSAUXC);  		tsim = rd32(IGC_TSIM);  		if (rq->perout.index == 1) { -			tsauxc &= ~(IGC_TSAUXC_EN_TT1 | IGC_TSAUXC_EN_CLK1); +			tsauxc &= ~(IGC_TSAUXC_EN_TT1 | IGC_TSAUXC_EN_CLK1 | +				    IGC_TSAUXC_ST1);  			tsim &= ~IGC_TSICR_TT1;  		} else { -			tsauxc &= ~(IGC_TSAUXC_EN_TT0 | IGC_TSAUXC_EN_CLK0); +			tsauxc &= ~(IGC_TSAUXC_EN_TT0 | IGC_TSAUXC_EN_CLK0 | +				    IGC_TSAUXC_ST0);  			tsim &= ~IGC_TSICR_TT0;  		}  		if (on) { @@ -415,10 +417,12 @@ static int igc_ptp_verify_pin(struct ptp_clock_info *ptp, unsigned int pin,   *   * We need to convert the system time value stored in the RX/TXSTMP registers   * into a hwtstamp which can be used by the upper level timestamping functions. + * + * Returns 0 on success.   **/ -static void igc_ptp_systim_to_hwtstamp(struct igc_adapter *adapter, -				       struct skb_shared_hwtstamps *hwtstamps, -				       u64 systim) +static int igc_ptp_systim_to_hwtstamp(struct igc_adapter *adapter, +				      struct skb_shared_hwtstamps *hwtstamps, +				      u64 systim)  {  	switch (adapter->hw.mac.type) {  	case igc_i225: @@ -428,8 +432,9 @@ static void igc_ptp_systim_to_hwtstamp(struct igc_adapter *adapter,  						systim & 0xFFFFFFFF);  		break;  	default: -		break; +		return -EINVAL;  	} +	return 0;  }  /** @@ -650,7 +655,8 @@ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter)  	regval = rd32(IGC_TXSTMPL);  	regval |= (u64)rd32(IGC_TXSTMPH) << 32; -	igc_ptp_systim_to_hwtstamp(adapter, &shhwtstamps, regval); +	if (igc_ptp_systim_to_hwtstamp(adapter, &shhwtstamps, regval)) +		return;  	switch (adapter->link_speed) {  	case SPEED_10: diff --git a/drivers/net/ethernet/intel/igc/igc_regs.h b/drivers/net/ethernet/intel/igc/igc_regs.h index c0d8214148d1..01c86d36856d 100644 --- a/drivers/net/ethernet/intel/igc/igc_regs.h +++ b/drivers/net/ethernet/intel/igc/igc_regs.h @@ -224,6 +224,7 @@  /* Transmit Scheduling Registers */  #define IGC_TQAVCTRL		0x3570  #define IGC_TXQCTL(_n)		(0x3344 + 0x4 * (_n)) +#define IGC_GTXOFFSET		0x3310  #define IGC_BASET_L		0x3314  #define IGC_BASET_H		0x3318  #define IGC_QBVCYCLET		0x331C diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.c b/drivers/net/ethernet/intel/igc/igc_tsn.c index 0fce22de2ab8..a386c8d61dbf 100644 --- a/drivers/net/ethernet/intel/igc/igc_tsn.c +++ b/drivers/net/ethernet/intel/igc/igc_tsn.c @@ -2,6 +2,7 @@  /* Copyright (c)  2019 Intel Corporation */  #include "igc.h" +#include "igc_hw.h"  #include "igc_tsn.h"  static bool is_any_launchtime(struct igc_adapter *adapter) @@ -36,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->base_time) +	if (adapter->qbv_enable)  		new_flags |= IGC_FLAG_TSN_QBV_ENABLED;  	if (is_any_launchtime(adapter)) @@ -48,6 +49,35 @@ static unsigned int igc_tsn_new_flags(struct igc_adapter *adapter)  	return new_flags;  } +void igc_tsn_adjust_txtime_offset(struct igc_adapter *adapter) +{ +	struct igc_hw *hw = &adapter->hw; +	u16 txoffset; + +	if (!is_any_launchtime(adapter)) +		return; + +	switch (adapter->link_speed) { +	case SPEED_10: +		txoffset = IGC_TXOFFSET_SPEED_10; +		break; +	case SPEED_100: +		txoffset = IGC_TXOFFSET_SPEED_100; +		break; +	case SPEED_1000: +		txoffset = IGC_TXOFFSET_SPEED_1000; +		break; +	case SPEED_2500: +		txoffset = IGC_TXOFFSET_SPEED_2500; +		break; +	default: +		txoffset = 0; +		break; +	} + +	wr32(IGC_GTXOFFSET, txoffset); +} +  /* Returns the TSN specific registers to their default values after   * the adapter is reset.   */ @@ -57,12 +87,14 @@ static int igc_tsn_disable_offload(struct igc_adapter *adapter)  	u32 tqavctrl;  	int i; +	wr32(IGC_GTXOFFSET, 0);  	wr32(IGC_TXPBS, I225_TXPBSIZE_DEFAULT);  	wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_DEFAULT);  	tqavctrl = rd32(IGC_TQAVCTRL);  	tqavctrl &= ~(IGC_TQAVCTRL_TRANSMIT_MODE_TSN | -		      IGC_TQAVCTRL_ENHANCED_QAV); +		      IGC_TQAVCTRL_ENHANCED_QAV | IGC_TQAVCTRL_FUTSCDDIS); +  	wr32(IGC_TQAVCTRL, tqavctrl);  	for (i = 0; i < adapter->num_tx_queues; i++) { @@ -87,20 +119,10 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter)  	ktime_t base_time, systim;  	int i; -	cycle = adapter->cycle_time; -	base_time = adapter->base_time; -  	wr32(IGC_TSAUXC, 0);  	wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_TSN);  	wr32(IGC_TXPBS, IGC_TXPBSIZE_TSN); -	tqavctrl = rd32(IGC_TQAVCTRL); -	tqavctrl |= IGC_TQAVCTRL_TRANSMIT_MODE_TSN | IGC_TQAVCTRL_ENHANCED_QAV; -	wr32(IGC_TQAVCTRL, tqavctrl); - -	wr32(IGC_QBVCYCLET_S, cycle); -	wr32(IGC_QBVCYCLET, cycle); -  	for (i = 0; i < adapter->num_tx_queues; i++) {  		struct igc_ring *ring = adapter->tx_ring[i];  		u32 txqctl = 0; @@ -110,15 +132,8 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter)  		wr32(IGC_STQT(i), ring->start_time);  		wr32(IGC_ENDQT(i), ring->end_time); -		if (adapter->base_time) { -			/* If we have a base_time 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. -			 */ -			txqctl |= IGC_TXQCTL_STRICT_CYCLE | -				IGC_TXQCTL_STRICT_END; -		} +		txqctl |= IGC_TXQCTL_STRICT_CYCLE | +			IGC_TXQCTL_STRICT_END;  		if (ring->launchtime_enable)  			txqctl |= IGC_TXQCTL_QUEUE_MODE_LAUNCHT; @@ -210,21 +225,46 @@ skip_cbs:  		wr32(IGC_TXQCTL(i), txqctl);  	} +	tqavctrl = rd32(IGC_TQAVCTRL) & ~IGC_TQAVCTRL_FUTSCDDIS; +	tqavctrl |= IGC_TQAVCTRL_TRANSMIT_MODE_TSN | IGC_TQAVCTRL_ENHANCED_QAV; + +	cycle = adapter->cycle_time; +	base_time = adapter->base_time; +  	nsec = rd32(IGC_SYSTIML);  	sec = rd32(IGC_SYSTIMH);  	systim = ktime_set(sec, nsec); -  	if (ktime_compare(systim, base_time) > 0) { -		s64 n; +		s64 n = div64_s64(ktime_sub_ns(systim, base_time), cycle); -		n = div64_s64(ktime_sub_ns(systim, base_time), cycle);  		base_time = ktime_add_ns(base_time, (n + 1) * cycle); +	} 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;  	} -	baset_h = div_s64_rem(base_time, NSEC_PER_SEC, &baset_l); +	wr32(IGC_TQAVCTRL, tqavctrl); +	wr32(IGC_QBVCYCLET_S, cycle); +	wr32(IGC_QBVCYCLET, cycle); + +	baset_h = div_s64_rem(base_time, NSEC_PER_SEC, &baset_l);  	wr32(IGC_BASET_H, baset_h); + +	/* In i226, Future base time is only supported when FutScdDis bit +	 * is enabled and only active for re-configuration. +	 * In this case, initialize the base time with zero to create +	 * "re-configuration" scenario then only set the desired base time. +	 */ +	if (tqavctrl & IGC_TQAVCTRL_FUTSCDDIS) +		wr32(IGC_BASET_L, 0);  	wr32(IGC_BASET_L, baset_l);  	return 0; @@ -251,17 +291,14 @@ int igc_tsn_reset(struct igc_adapter *adapter)  int igc_tsn_offload_apply(struct igc_adapter *adapter)  { -	int err; +	struct igc_hw *hw = &adapter->hw; -	if (netif_running(adapter->netdev)) { +	if (netif_running(adapter->netdev) && igc_is_device_id_i225(hw)) {  		schedule_work(&adapter->reset_task);  		return 0;  	} -	err = igc_tsn_enable_offload(adapter); -	if (err < 0) -		return err; +	igc_tsn_reset(adapter); -	adapter->flags = igc_tsn_new_flags(adapter);  	return 0;  } diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.h b/drivers/net/ethernet/intel/igc/igc_tsn.h index 1512307f5a52..b53e6af560b7 100644 --- a/drivers/net/ethernet/intel/igc/igc_tsn.h +++ b/drivers/net/ethernet/intel/igc/igc_tsn.h @@ -6,5 +6,6 @@  int igc_tsn_offload_apply(struct igc_adapter *adapter);  int igc_tsn_reset(struct igc_adapter *adapter); +void igc_tsn_adjust_txtime_offset(struct igc_adapter *adapter);  #endif /* _IGC_BASE_H */ diff --git a/drivers/net/ethernet/intel/igc/igc_xdp.c b/drivers/net/ethernet/intel/igc/igc_xdp.c index aeeb34e64610..e27af72aada8 100644 --- a/drivers/net/ethernet/intel/igc/igc_xdp.c +++ b/drivers/net/ethernet/intel/igc/igc_xdp.c @@ -29,6 +29,11 @@ int igc_xdp_set_prog(struct igc_adapter *adapter, struct bpf_prog *prog,  	if (old_prog)  		bpf_prog_put(old_prog); +	if (prog) +		xdp_features_set_redirect_target(dev, true); +	else +		xdp_features_clear_redirect_target(dev); +  	if (if_running)  		igc_open(dev);  |