diff options
Diffstat (limited to 'drivers/net/ethernet/intel/igc')
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc.h | 52 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_base.c | 10 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_defines.h | 91 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_ethtool.c | 49 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_main.c | 504 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_phy.c | 6 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_ptp.c | 182 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_regs.h | 43 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_tsn.c | 176 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_tsn.h | 1 | 
10 files changed, 992 insertions, 122 deletions
| diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h index 9e0bbb2e55e3..3e386c38d016 100644 --- a/drivers/net/ethernet/intel/igc/igc.h +++ b/drivers/net/ethernet/intel/igc/igc.h @@ -33,6 +33,8 @@ void igc_ethtool_set_ops(struct net_device *);  #define IGC_N_PEROUT	2  #define IGC_N_SDP	4 +#define MAX_FLEX_FILTER			32 +  enum igc_mac_filter_type {  	IGC_MAC_FILTER_TYPE_DST = 0,  	IGC_MAC_FILTER_TYPE_SRC @@ -96,6 +98,13 @@ struct igc_ring {  	u32 start_time;  	u32 end_time; +	/* CBS parameters */ +	bool cbs_enable;                /* indicates if CBS is enabled */ +	s32 idleslope;                  /* idleSlope in kbps */ +	s32 sendslope;                  /* sendSlope in kbps */ +	s32 hicredit;                   /* hiCredit in bytes */ +	s32 locredit;                   /* loCredit in bytes */ +  	/* everything past this point are written often */  	u16 next_to_clean;  	u16 next_to_use; @@ -225,6 +234,7 @@ struct igc_adapter {  	struct timecounter tc;  	struct timespec64 prev_ptp_time; /* Pre-reset PTP clock */  	ktime_t ptp_reset_start; /* Reset time in clock mono */ +	struct system_time_snapshot snapshot;  	char fw_version[32]; @@ -287,6 +297,10 @@ extern char igc_driver_name[];  #define IGC_FLAG_VLAN_PROMISC		BIT(15)  #define IGC_FLAG_RX_LEGACY		BIT(16)  #define IGC_FLAG_TSN_QBV_ENABLED	BIT(17) +#define IGC_FLAG_TSN_QAV_ENABLED	BIT(18) + +#define IGC_FLAG_TSN_ANY_ENABLED \ +	(IGC_FLAG_TSN_QBV_ENABLED | IGC_FLAG_TSN_QAV_ENABLED)  #define IGC_FLAG_RSS_FIELD_IPV4_UDP	BIT(6)  #define IGC_FLAG_RSS_FIELD_IPV6_UDP	BIT(7) @@ -476,18 +490,28 @@ struct igc_q_vector {  };  enum igc_filter_match_flags { -	IGC_FILTER_FLAG_ETHER_TYPE =	0x1, -	IGC_FILTER_FLAG_VLAN_TCI   =	0x2, -	IGC_FILTER_FLAG_SRC_MAC_ADDR =	0x4, -	IGC_FILTER_FLAG_DST_MAC_ADDR =	0x8, +	IGC_FILTER_FLAG_ETHER_TYPE =	BIT(0), +	IGC_FILTER_FLAG_VLAN_TCI   =	BIT(1), +	IGC_FILTER_FLAG_SRC_MAC_ADDR =	BIT(2), +	IGC_FILTER_FLAG_DST_MAC_ADDR =	BIT(3), +	IGC_FILTER_FLAG_USER_DATA =	BIT(4), +	IGC_FILTER_FLAG_VLAN_ETYPE =	BIT(5),  };  struct igc_nfc_filter {  	u8 match_flags;  	u16 etype; +	__be16 vlan_etype;  	u16 vlan_tci;  	u8 src_addr[ETH_ALEN];  	u8 dst_addr[ETH_ALEN]; +	u8 user_data[8]; +	u8 user_mask[8]; +	u8 flex_index; +	u8 rx_queue; +	u8 prio; +	u8 immediate_irq; +	u8 drop;  };  struct igc_nfc_rule { @@ -495,12 +519,24 @@ struct igc_nfc_rule {  	struct igc_nfc_filter filter;  	u32 location;  	u16 action; +	bool flex;  }; -/* IGC supports a total of 32 NFC rules: 16 MAC address based,, 8 VLAN priority - * based, and 8 ethertype based. +/* IGC supports a total of 32 NFC rules: 16 MAC address based, 8 VLAN priority + * based, 8 ethertype based and 32 Flex filter based rules.   */ -#define IGC_MAX_RXNFC_RULES		32 +#define IGC_MAX_RXNFC_RULES		64 + +struct igc_flex_filter { +	u8 index; +	u8 data[128]; +	u8 mask[16]; +	u8 length; +	u8 rx_queue; +	u8 prio; +	u8 immediate_irq; +	u8 drop; +};  /* igc_desc_unused - calculate if we have unused descriptors */  static inline u16 igc_desc_unused(const struct igc_ring *ring) @@ -578,7 +614,7 @@ static inline s32 igc_read_phy_reg(struct igc_hw *hw, u32 offset, u16 *data)  	if (hw->phy.ops.read_reg)  		return hw->phy.ops.read_reg(hw, offset, data); -	return 0; +	return -EOPNOTSUPP;  }  void igc_reinit_locked(struct igc_adapter *); diff --git a/drivers/net/ethernet/intel/igc/igc_base.c b/drivers/net/ethernet/intel/igc/igc_base.c index d0700d48ecf9..84f142f5e472 100644 --- a/drivers/net/ethernet/intel/igc/igc_base.c +++ b/drivers/net/ethernet/intel/igc/igc_base.c @@ -187,15 +187,7 @@ static s32 igc_init_phy_params_base(struct igc_hw *hw)  	igc_check_for_copper_link(hw); -	/* Verify phy id and set remaining function pointers */ -	switch (phy->id) { -	case I225_I_PHY_ID: -		phy->type	= igc_phy_i225; -		break; -	default: -		ret_val = -IGC_ERR_PHY; -		goto out; -	} +	phy->type = igc_phy_i225;  out:  	return ret_val; diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h index c3a5a5518790..a4bbee748798 100644 --- a/drivers/net/ethernet/intel/igc/igc_defines.h +++ b/drivers/net/ethernet/intel/igc/igc_defines.h @@ -17,11 +17,22 @@  #define IGC_WUC_PME_EN	0x00000002 /* PME Enable */  /* Wake Up Filter Control */ -#define IGC_WUFC_LNKC	0x00000001 /* Link Status Change Wakeup Enable */ -#define IGC_WUFC_MAG	0x00000002 /* Magic Packet Wakeup Enable */ -#define IGC_WUFC_EX	0x00000004 /* Directed Exact Wakeup Enable */ -#define IGC_WUFC_MC	0x00000008 /* Directed Multicast Wakeup Enable */ -#define IGC_WUFC_BC	0x00000010 /* Broadcast Wakeup Enable */ +#define IGC_WUFC_LNKC		0x00000001 /* Link Status Change Wakeup Enable */ +#define IGC_WUFC_MAG		0x00000002 /* Magic Packet Wakeup Enable */ +#define IGC_WUFC_EX		0x00000004 /* Directed Exact Wakeup Enable */ +#define IGC_WUFC_MC		0x00000008 /* Directed Multicast Wakeup Enable */ +#define IGC_WUFC_BC		0x00000010 /* Broadcast Wakeup Enable */ +#define IGC_WUFC_FLEX_HQ	BIT(14)	   /* Flex Filters Host Queuing */ +#define IGC_WUFC_FLX0		BIT(16)	   /* Flexible Filter 0 Enable */ +#define IGC_WUFC_FLX1		BIT(17)	   /* Flexible Filter 1 Enable */ +#define IGC_WUFC_FLX2		BIT(18)	   /* Flexible Filter 2 Enable */ +#define IGC_WUFC_FLX3		BIT(19)	   /* Flexible Filter 3 Enable */ +#define IGC_WUFC_FLX4		BIT(20)	   /* Flexible Filter 4 Enable */ +#define IGC_WUFC_FLX5		BIT(21)	   /* Flexible Filter 5 Enable */ +#define IGC_WUFC_FLX6		BIT(22)	   /* Flexible Filter 6 Enable */ +#define IGC_WUFC_FLX7		BIT(23)	   /* Flexible Filter 7 Enable */ + +#define IGC_WUFC_FILTER_MASK GENMASK(23, 14)  #define IGC_CTRL_ADVD3WUC	0x00100000  /* D3 WUC */ @@ -46,6 +57,37 @@  /* Wake Up Packet Memory stores the first 128 bytes of the wake up packet */  #define IGC_WUPM_BYTES	128 +/* Wakeup Filter Control Extended */ +#define IGC_WUFC_EXT_FLX8	BIT(8)	/* Flexible Filter 8 Enable */ +#define IGC_WUFC_EXT_FLX9	BIT(9)	/* Flexible Filter 9 Enable */ +#define IGC_WUFC_EXT_FLX10	BIT(10)	/* Flexible Filter 10 Enable */ +#define IGC_WUFC_EXT_FLX11	BIT(11)	/* Flexible Filter 11 Enable */ +#define IGC_WUFC_EXT_FLX12	BIT(12)	/* Flexible Filter 12 Enable */ +#define IGC_WUFC_EXT_FLX13	BIT(13)	/* Flexible Filter 13 Enable */ +#define IGC_WUFC_EXT_FLX14	BIT(14)	/* Flexible Filter 14 Enable */ +#define IGC_WUFC_EXT_FLX15	BIT(15)	/* Flexible Filter 15 Enable */ +#define IGC_WUFC_EXT_FLX16	BIT(16)	/* Flexible Filter 16 Enable */ +#define IGC_WUFC_EXT_FLX17	BIT(17)	/* Flexible Filter 17 Enable */ +#define IGC_WUFC_EXT_FLX18	BIT(18)	/* Flexible Filter 18 Enable */ +#define IGC_WUFC_EXT_FLX19	BIT(19)	/* Flexible Filter 19 Enable */ +#define IGC_WUFC_EXT_FLX20	BIT(20)	/* Flexible Filter 20 Enable */ +#define IGC_WUFC_EXT_FLX21	BIT(21)	/* Flexible Filter 21 Enable */ +#define IGC_WUFC_EXT_FLX22	BIT(22)	/* Flexible Filter 22 Enable */ +#define IGC_WUFC_EXT_FLX23	BIT(23)	/* Flexible Filter 23 Enable */ +#define IGC_WUFC_EXT_FLX24	BIT(24)	/* Flexible Filter 24 Enable */ +#define IGC_WUFC_EXT_FLX25	BIT(25)	/* Flexible Filter 25 Enable */ +#define IGC_WUFC_EXT_FLX26	BIT(26)	/* Flexible Filter 26 Enable */ +#define IGC_WUFC_EXT_FLX27	BIT(27)	/* Flexible Filter 27 Enable */ +#define IGC_WUFC_EXT_FLX28	BIT(28)	/* Flexible Filter 28 Enable */ +#define IGC_WUFC_EXT_FLX29	BIT(29)	/* Flexible Filter 29 Enable */ +#define IGC_WUFC_EXT_FLX30	BIT(30)	/* Flexible Filter 30 Enable */ +#define IGC_WUFC_EXT_FLX31	BIT(31)	/* Flexible Filter 31 Enable */ + +#define IGC_WUFC_EXT_FILTER_MASK GENMASK(31, 8) + +/* Physical Func Reset Done Indication */ +#define IGC_CTRL_EXT_LINK_MODE_MASK	0x00C00000 +  /* Loop limit on how long we wait for auto-negotiation to complete */  #define COPPER_LINK_UP_LIMIT		10  #define PHY_AUTO_NEG_LIMIT		45 @@ -476,11 +518,50 @@  #define IGC_TXQCTL_QUEUE_MODE_LAUNCHT	0x00000001  #define IGC_TXQCTL_STRICT_CYCLE		0x00000002  #define IGC_TXQCTL_STRICT_END		0x00000004 +#define IGC_TXQCTL_QAV_SEL_MASK		0x000000C0 +#define IGC_TXQCTL_QAV_SEL_CBS0		0x00000080 +#define IGC_TXQCTL_QAV_SEL_CBS1		0x000000C0 + +#define IGC_TQAVCC_IDLESLOPE_MASK	0xFFFF +#define IGC_TQAVCC_KEEP_CREDITS		BIT(30) + +#define IGC_MAX_SR_QUEUES		2  /* Receive Checksum Control */  #define IGC_RXCSUM_CRCOFL	0x00000800   /* CRC32 offload enable */  #define IGC_RXCSUM_PCSD		0x00002000   /* packet checksum disabled */ +/* PCIe PTM Control */ +#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_PTM_TO(usec)	(((usec) & 0xff) << 8) + +#define IGC_PTM_SHORT_CYC_DEFAULT	10  /* Default Short/interrupted cycle interval */ +#define IGC_PTM_CYC_TIME_DEFAULT	5   /* Default PTM cycle time */ +#define IGC_PTM_TIMEOUT_DEFAULT		255 /* Default timeout for PTM errors */ + +/* PCIe Digital Delay */ +#define IGC_PCIE_DIG_DELAY_DEFAULT	0x01440000 + +/* PCIe PHY Delay */ +#define IGC_PCIE_PHY_DELAY_DEFAULT	0x40900000 + +#define IGC_TIMADJ_ADJUST_METH		0x40000000 + +/* PCIe PTM Status */ +#define IGC_PTM_STAT_VALID		BIT(0) /* PTM Status */ +#define IGC_PTM_STAT_RET_ERR		BIT(1) /* Root port timeout */ +#define IGC_PTM_STAT_BAD_PTM_RES	BIT(2) /* PTM Response msg instead of PTM Response Data */ +#define IGC_PTM_STAT_T4M1_OVFL		BIT(3) /* T4 minus T1 overflow */ +#define IGC_PTM_STAT_ADJUST_1ST		BIT(4) /* 1588 timer adjusted during 1st PTM cycle */ +#define IGC_PTM_STAT_ADJUST_CYC		BIT(5) /* 1588 timer adjusted during non-1st PTM cycle */ + +/* PCIe PTM Cycle Control */ +#define IGC_PTM_CYCLE_CTRL_CYC_TIME(msec)	((msec) & 0x3ff) /* PTM Cycle Time (msec) */ +#define IGC_PTM_CYCLE_CTRL_AUTO_CYC_EN		BIT(31) /* PTM Cycle Control */ +  /* GPY211 - I225 defines */  #define GPY_MMD_MASK		0xFFFF0000  #define GPY_MMD_SHIFT		16 diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c index fa4171860623..e0a76ac1bbbc 100644 --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c @@ -862,7 +862,9 @@ static void igc_ethtool_get_stats(struct net_device *netdev,  }  static int igc_ethtool_get_coalesce(struct net_device *netdev, -				    struct ethtool_coalesce *ec) +				    struct ethtool_coalesce *ec, +				    struct kernel_ethtool_coalesce *kernel_coal, +				    struct netlink_ext_ack *extack)  {  	struct igc_adapter *adapter = netdev_priv(netdev); @@ -882,7 +884,9 @@ static int igc_ethtool_get_coalesce(struct net_device *netdev,  }  static int igc_ethtool_set_coalesce(struct net_device *netdev, -				    struct ethtool_coalesce *ec) +				    struct ethtool_coalesce *ec, +				    struct kernel_ethtool_coalesce *kernel_coal, +				    struct netlink_ext_ack *extack)  {  	struct igc_adapter *adapter = netdev_priv(netdev);  	int i; @@ -979,6 +983,12 @@ static int igc_ethtool_get_nfc_rule(struct igc_adapter *adapter,  		eth_broadcast_addr(fsp->m_u.ether_spec.h_source);  	} +	if (rule->filter.match_flags & IGC_FILTER_FLAG_USER_DATA) { +		fsp->flow_type |= FLOW_EXT; +		memcpy(fsp->h_ext.data, rule->filter.user_data, sizeof(fsp->h_ext.data)); +		memcpy(fsp->m_ext.data, rule->filter.user_mask, sizeof(fsp->m_ext.data)); +	} +  	mutex_unlock(&adapter->nfc_rule_lock);  	return 0; @@ -1215,6 +1225,30 @@ static void igc_ethtool_init_nfc_rule(struct igc_nfc_rule *rule,  		ether_addr_copy(rule->filter.dst_addr,  				fsp->h_u.ether_spec.h_dest);  	} + +	/* VLAN etype matching */ +	if ((fsp->flow_type & FLOW_EXT) && fsp->h_ext.vlan_etype) { +		rule->filter.vlan_etype = fsp->h_ext.vlan_etype; +		rule->filter.match_flags |= IGC_FILTER_FLAG_VLAN_ETYPE; +	} + +	/* Check for user defined data */ +	if ((fsp->flow_type & FLOW_EXT) && +	    (fsp->h_ext.data[0] || fsp->h_ext.data[1])) { +		rule->filter.match_flags |= IGC_FILTER_FLAG_USER_DATA; +		memcpy(rule->filter.user_data, fsp->h_ext.data, sizeof(fsp->h_ext.data)); +		memcpy(rule->filter.user_mask, fsp->m_ext.data, sizeof(fsp->m_ext.data)); +	} + +	/* When multiple filter options or user data or vlan etype is set, use a +	 * flex filter. +	 */ +	if ((rule->filter.match_flags & IGC_FILTER_FLAG_USER_DATA) || +	    (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_ETYPE) || +	    (rule->filter.match_flags & (rule->filter.match_flags - 1))) +		rule->flex = true; +	else +		rule->flex = false;  }  /** @@ -1244,11 +1278,6 @@ static int igc_ethtool_check_nfc_rule(struct igc_adapter *adapter,  		return -EINVAL;  	} -	if (flags & (flags - 1)) { -		netdev_dbg(dev, "Rule with multiple matches not supported\n"); -		return -EOPNOTSUPP; -	} -  	list_for_each_entry(tmp, &adapter->nfc_rule_list, list) {  		if (!memcmp(&rule->filter, &tmp->filter,  			    sizeof(rule->filter)) && @@ -1280,12 +1309,6 @@ static int igc_ethtool_add_nfc_rule(struct igc_adapter *adapter,  		return -EOPNOTSUPP;  	} -	if ((fsp->flow_type & FLOW_EXT) && -	    fsp->m_ext.vlan_tci != htons(VLAN_PRIO_MASK)) { -		netdev_dbg(netdev, "VLAN mask not supported\n"); -		return -EOPNOTSUPP; -	} -  	if (fsp->ring_cookie >= adapter->num_rx_queues) {  		netdev_dbg(netdev, "Invalid action\n");  		return -EINVAL; diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 95323095094d..b877efae61df 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -12,6 +12,8 @@  #include <net/pkt_sched.h>  #include <linux/bpf_trace.h>  #include <net/xdp_sock_drv.h> +#include <linux/pci.h> +  #include <net/ipv6.h>  #include "igc.h" @@ -118,7 +120,7 @@ void igc_reset(struct igc_adapter *adapter)  	igc_ptp_reset(adapter);  	/* Re-enable TSN offloading, where applicable. */ -	igc_tsn_offload_apply(adapter); +	igc_tsn_reset(adapter);  	igc_get_phy_info(hw);  } @@ -149,6 +151,9 @@ static void igc_release_hw_control(struct igc_adapter *adapter)  	struct igc_hw *hw = &adapter->hw;  	u32 ctrl_ext; +	if (!pci_device_is_present(adapter->pdev)) +		return; +  	/* Let firmware take over control of h/w */  	ctrl_ext = rd32(IGC_CTRL_EXT);  	wr32(IGC_CTRL_EXT, @@ -232,6 +237,8 @@ static void igc_clean_tx_ring(struct igc_ring *tx_ring)  				igc_unmap_tx_buffer(tx_ring->dev, tx_buffer);  		} +		tx_buffer->next_to_watch = NULL; +  		/* move us one more past the eop_desc for start of next pkt */  		tx_buffer++;  		i++; @@ -3073,11 +3080,320 @@ static void igc_del_etype_filter(struct igc_adapter *adapter, u16 etype)  		   etype);  } +static int igc_flex_filter_select(struct igc_adapter *adapter, +				  struct igc_flex_filter *input, +				  u32 *fhft) +{ +	struct igc_hw *hw = &adapter->hw; +	u8 fhft_index; +	u32 fhftsl; + +	if (input->index >= MAX_FLEX_FILTER) { +		dev_err(&adapter->pdev->dev, "Wrong Flex Filter index selected!\n"); +		return -EINVAL; +	} + +	/* Indirect table select register */ +	fhftsl = rd32(IGC_FHFTSL); +	fhftsl &= ~IGC_FHFTSL_FTSL_MASK; +	switch (input->index) { +	case 0 ... 7: +		fhftsl |= 0x00; +		break; +	case 8 ... 15: +		fhftsl |= 0x01; +		break; +	case 16 ... 23: +		fhftsl |= 0x02; +		break; +	case 24 ... 31: +		fhftsl |= 0x03; +		break; +	} +	wr32(IGC_FHFTSL, fhftsl); + +	/* Normalize index down to host table register */ +	fhft_index = input->index % 8; + +	*fhft = (fhft_index < 4) ? IGC_FHFT(fhft_index) : +		IGC_FHFT_EXT(fhft_index - 4); + +	return 0; +} + +static int igc_write_flex_filter_ll(struct igc_adapter *adapter, +				    struct igc_flex_filter *input) +{ +	struct device *dev = &adapter->pdev->dev; +	struct igc_hw *hw = &adapter->hw; +	u8 *data = input->data; +	u8 *mask = input->mask; +	u32 queuing; +	u32 fhft; +	u32 wufc; +	int ret; +	int i; + +	/* Length has to be aligned to 8. Otherwise the filter will fail. Bail +	 * out early to avoid surprises later. +	 */ +	if (input->length % 8 != 0) { +		dev_err(dev, "The length of a flex filter has to be 8 byte aligned!\n"); +		return -EINVAL; +	} + +	/* Select corresponding flex filter register and get base for host table. */ +	ret = igc_flex_filter_select(adapter, input, &fhft); +	if (ret) +		return ret; + +	/* When adding a filter globally disable flex filter feature. That is +	 * recommended within the datasheet. +	 */ +	wufc = rd32(IGC_WUFC); +	wufc &= ~IGC_WUFC_FLEX_HQ; +	wr32(IGC_WUFC, wufc); + +	/* Configure filter */ +	queuing = input->length & IGC_FHFT_LENGTH_MASK; +	queuing |= (input->rx_queue << IGC_FHFT_QUEUE_SHIFT) & IGC_FHFT_QUEUE_MASK; +	queuing |= (input->prio << IGC_FHFT_PRIO_SHIFT) & IGC_FHFT_PRIO_MASK; + +	if (input->immediate_irq) +		queuing |= IGC_FHFT_IMM_INT; + +	if (input->drop) +		queuing |= IGC_FHFT_DROP; + +	wr32(fhft + 0xFC, queuing); + +	/* Write data (128 byte) and mask (128 bit) */ +	for (i = 0; i < 16; ++i) { +		const size_t data_idx = i * 8; +		const size_t row_idx = i * 16; +		u32 dw0 = +			(data[data_idx + 0] << 0) | +			(data[data_idx + 1] << 8) | +			(data[data_idx + 2] << 16) | +			(data[data_idx + 3] << 24); +		u32 dw1 = +			(data[data_idx + 4] << 0) | +			(data[data_idx + 5] << 8) | +			(data[data_idx + 6] << 16) | +			(data[data_idx + 7] << 24); +		u32 tmp; + +		/* Write row: dw0, dw1 and mask */ +		wr32(fhft + row_idx, dw0); +		wr32(fhft + row_idx + 4, dw1); + +		/* mask is only valid for MASK(7, 0) */ +		tmp = rd32(fhft + row_idx + 8); +		tmp &= ~GENMASK(7, 0); +		tmp |= mask[i]; +		wr32(fhft + row_idx + 8, tmp); +	} + +	/* Enable filter. */ +	wufc |= IGC_WUFC_FLEX_HQ; +	if (input->index > 8) { +		/* Filter 0-7 are enabled via WUFC. The other 24 filters are not. */ +		u32 wufc_ext = rd32(IGC_WUFC_EXT); + +		wufc_ext |= (IGC_WUFC_EXT_FLX8 << (input->index - 8)); + +		wr32(IGC_WUFC_EXT, wufc_ext); +	} else { +		wufc |= (IGC_WUFC_FLX0 << input->index); +	} +	wr32(IGC_WUFC, wufc); + +	dev_dbg(&adapter->pdev->dev, "Added flex filter %u to HW.\n", +		input->index); + +	return 0; +} + +static void igc_flex_filter_add_field(struct igc_flex_filter *flex, +				      const void *src, unsigned int offset, +				      size_t len, const void *mask) +{ +	int i; + +	/* data */ +	memcpy(&flex->data[offset], src, len); + +	/* mask */ +	for (i = 0; i < len; ++i) { +		const unsigned int idx = i + offset; +		const u8 *ptr = mask; + +		if (mask) { +			if (ptr[i] & 0xff) +				flex->mask[idx / 8] |= BIT(idx % 8); + +			continue; +		} + +		flex->mask[idx / 8] |= BIT(idx % 8); +	} +} + +static int igc_find_avail_flex_filter_slot(struct igc_adapter *adapter) +{ +	struct igc_hw *hw = &adapter->hw; +	u32 wufc, wufc_ext; +	int i; + +	wufc = rd32(IGC_WUFC); +	wufc_ext = rd32(IGC_WUFC_EXT); + +	for (i = 0; i < MAX_FLEX_FILTER; i++) { +		if (i < 8) { +			if (!(wufc & (IGC_WUFC_FLX0 << i))) +				return i; +		} else { +			if (!(wufc_ext & (IGC_WUFC_EXT_FLX8 << (i - 8)))) +				return i; +		} +	} + +	return -ENOSPC; +} + +static bool igc_flex_filter_in_use(struct igc_adapter *adapter) +{ +	struct igc_hw *hw = &adapter->hw; +	u32 wufc, wufc_ext; + +	wufc = rd32(IGC_WUFC); +	wufc_ext = rd32(IGC_WUFC_EXT); + +	if (wufc & IGC_WUFC_FILTER_MASK) +		return true; + +	if (wufc_ext & IGC_WUFC_EXT_FILTER_MASK) +		return true; + +	return false; +} + +static int igc_add_flex_filter(struct igc_adapter *adapter, +			       struct igc_nfc_rule *rule) +{ +	struct igc_flex_filter flex = { }; +	struct igc_nfc_filter *filter = &rule->filter; +	unsigned int eth_offset, user_offset; +	int ret, index; +	bool vlan; + +	index = igc_find_avail_flex_filter_slot(adapter); +	if (index < 0) +		return -ENOSPC; + +	/* Construct the flex filter: +	 *  -> dest_mac [6] +	 *  -> src_mac [6] +	 *  -> tpid [2] +	 *  -> vlan tci [2] +	 *  -> ether type [2] +	 *  -> user data [8] +	 *  -> = 26 bytes => 32 length +	 */ +	flex.index    = index; +	flex.length   = 32; +	flex.rx_queue = rule->action; + +	vlan = rule->filter.vlan_tci || rule->filter.vlan_etype; +	eth_offset = vlan ? 16 : 12; +	user_offset = vlan ? 18 : 14; + +	/* Add destination MAC  */ +	if (rule->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR) +		igc_flex_filter_add_field(&flex, &filter->dst_addr, 0, +					  ETH_ALEN, NULL); + +	/* Add source MAC */ +	if (rule->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR) +		igc_flex_filter_add_field(&flex, &filter->src_addr, 6, +					  ETH_ALEN, NULL); + +	/* Add VLAN etype */ +	if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_ETYPE) +		igc_flex_filter_add_field(&flex, &filter->vlan_etype, 12, +					  sizeof(filter->vlan_etype), +					  NULL); + +	/* Add VLAN TCI */ +	if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) +		igc_flex_filter_add_field(&flex, &filter->vlan_tci, 14, +					  sizeof(filter->vlan_tci), NULL); + +	/* Add Ether type */ +	if (rule->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE) { +		__be16 etype = cpu_to_be16(filter->etype); + +		igc_flex_filter_add_field(&flex, &etype, eth_offset, +					  sizeof(etype), NULL); +	} + +	/* Add user data */ +	if (rule->filter.match_flags & IGC_FILTER_FLAG_USER_DATA) +		igc_flex_filter_add_field(&flex, &filter->user_data, +					  user_offset, +					  sizeof(filter->user_data), +					  filter->user_mask); + +	/* Add it down to the hardware and enable it. */ +	ret = igc_write_flex_filter_ll(adapter, &flex); +	if (ret) +		return ret; + +	filter->flex_index = index; + +	return 0; +} + +static void igc_del_flex_filter(struct igc_adapter *adapter, +				u16 reg_index) +{ +	struct igc_hw *hw = &adapter->hw; +	u32 wufc; + +	/* Just disable the filter. The filter table itself is kept +	 * intact. Another flex_filter_add() should override the "old" data +	 * then. +	 */ +	if (reg_index > 8) { +		u32 wufc_ext = rd32(IGC_WUFC_EXT); + +		wufc_ext &= ~(IGC_WUFC_EXT_FLX8 << (reg_index - 8)); +		wr32(IGC_WUFC_EXT, wufc_ext); +	} else { +		wufc = rd32(IGC_WUFC); + +		wufc &= ~(IGC_WUFC_FLX0 << reg_index); +		wr32(IGC_WUFC, wufc); +	} + +	if (igc_flex_filter_in_use(adapter)) +		return; + +	/* No filters are in use, we may disable flex filters */ +	wufc = rd32(IGC_WUFC); +	wufc &= ~IGC_WUFC_FLEX_HQ; +	wr32(IGC_WUFC, wufc); +} +  static int igc_enable_nfc_rule(struct igc_adapter *adapter, -			       const struct igc_nfc_rule *rule) +			       struct igc_nfc_rule *rule)  {  	int err; +	if (rule->flex) { +		return igc_add_flex_filter(adapter, rule); +	} +  	if (rule->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE) {  		err = igc_add_etype_filter(adapter, rule->filter.etype,  					   rule->action); @@ -3114,6 +3430,11 @@ static int igc_enable_nfc_rule(struct igc_adapter *adapter,  static void igc_disable_nfc_rule(struct igc_adapter *adapter,  				 const struct igc_nfc_rule *rule)  { +	if (rule->flex) { +		igc_del_flex_filter(adapter, rule->filter.flex_index); +		return; +	} +  	if (rule->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE)  		igc_del_etype_filter(adapter, rule->filter.etype); @@ -4447,26 +4768,29 @@ void igc_down(struct igc_adapter *adapter)  	igc_ptp_suspend(adapter); -	/* disable receives in the hardware */ -	rctl = rd32(IGC_RCTL); -	wr32(IGC_RCTL, rctl & ~IGC_RCTL_EN); -	/* flush and sleep below */ - +	if (pci_device_is_present(adapter->pdev)) { +		/* disable receives in the hardware */ +		rctl = rd32(IGC_RCTL); +		wr32(IGC_RCTL, rctl & ~IGC_RCTL_EN); +		/* flush and sleep below */ +	}  	/* set trans_start so we don't get spurious watchdogs during reset */  	netif_trans_update(netdev);  	netif_carrier_off(netdev);  	netif_tx_stop_all_queues(netdev); -	/* disable transmits in the hardware */ -	tctl = rd32(IGC_TCTL); -	tctl &= ~IGC_TCTL_EN; -	wr32(IGC_TCTL, tctl); -	/* flush both disables and wait for them to finish */ -	wrfl(); -	usleep_range(10000, 20000); +	if (pci_device_is_present(adapter->pdev)) { +		/* disable transmits in the hardware */ +		tctl = rd32(IGC_TCTL); +		tctl &= ~IGC_TCTL_EN; +		wr32(IGC_TCTL, tctl); +		/* flush both disables and wait for them to finish */ +		wrfl(); +		usleep_range(10000, 20000); -	igc_irq_disable(adapter); +		igc_irq_disable(adapter); +	}  	adapter->flags &= ~IGC_FLAG_NEED_LINK_UPDATE; @@ -4809,6 +5133,7 @@ static irqreturn_t igc_msix_ring(int irq, void *data)   */  static int igc_request_msix(struct igc_adapter *adapter)  { +	unsigned int num_q_vectors = adapter->num_q_vectors;  	int i = 0, err = 0, vector = 0, free_vector = 0;  	struct net_device *netdev = adapter->netdev; @@ -4817,7 +5142,13 @@ static int igc_request_msix(struct igc_adapter *adapter)  	if (err)  		goto err_out; -	for (i = 0; i < adapter->num_q_vectors; i++) { +	if (num_q_vectors > MAX_Q_VECTORS) { +		num_q_vectors = MAX_Q_VECTORS; +		dev_warn(&adapter->pdev->dev, +			 "The number of queue vectors (%d) is higher than max allowed (%d)\n", +			 adapter->num_q_vectors, MAX_Q_VECTORS); +	} +	for (i = 0; i < num_q_vectors; i++) {  		struct igc_q_vector *q_vector = adapter->q_vector[i];  		vector++; @@ -4896,20 +5227,12 @@ bool igc_has_link(struct igc_adapter *adapter)  	 * false until the igc_check_for_link establishes link  	 * for copper adapters ONLY  	 */ -	switch (hw->phy.media_type) { -	case igc_media_type_copper: -		if (!hw->mac.get_link_status) -			return true; -		hw->mac.ops.check_for_link(hw); -		link_active = !hw->mac.get_link_status; -		break; -	default: -	case igc_media_type_unknown: -		break; -	} +	if (!hw->mac.get_link_status) +		return true; +	hw->mac.ops.check_for_link(hw); +	link_active = !hw->mac.get_link_status; -	if (hw->mac.type == igc_i225 && -	    hw->phy.id == I225_I_PHY_ID) { +	if (hw->mac.type == igc_i225) {  		if (!netif_carrier_ok(adapter->netdev)) {  			adapter->flags &= ~IGC_FLAG_NEED_LINK_UPDATE;  		} else if (!(adapter->flags & IGC_FLAG_NEED_LINK_UPDATE)) { @@ -4997,7 +5320,9 @@ static void igc_watchdog_task(struct work_struct *work)  				adapter->tx_timeout_factor = 14;  				break;  			case SPEED_100: -				/* maybe add some timeout factor ? */ +			case SPEED_1000: +			case SPEED_2500: +				adapter->tx_timeout_factor = 7;  				break;  			} @@ -5424,7 +5749,6 @@ static int igc_save_launchtime_params(struct igc_adapter *adapter, int queue,  				      bool enable)  {  	struct igc_ring *ring; -	int i;  	if (queue < 0 || queue >= adapter->num_tx_queues)  		return -EINVAL; @@ -5432,17 +5756,6 @@ static int igc_save_launchtime_params(struct igc_adapter *adapter, int queue,  	ring = adapter->tx_ring[queue];  	ring->launchtime_enable = enable; -	if (adapter->base_time) -		return 0; - -	adapter->cycle_time = NSEC_PER_SEC; - -	for (i = 0; i < adapter->num_tx_queues; i++) { -		ring = adapter->tx_ring[i]; -		ring->start_time = 0; -		ring->end_time = NSEC_PER_SEC; -	} -  	return 0;  } @@ -5487,7 +5800,7 @@ static bool validate_schedule(struct igc_adapter *adapter,  		if (e->command != TC_TAPRIO_CMD_SET_GATES)  			return false; -		for (i = 0; i < IGC_MAX_TX_QUEUES; i++) { +		for (i = 0; i < adapter->num_tx_queues; i++) {  			if (e->gate_mask & BIT(i))  				queue_uses[i]++; @@ -5515,16 +5828,31 @@ 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) +{ +	int i; + +	adapter->base_time = 0; +	adapter->cycle_time = NSEC_PER_SEC; + +	for (i = 0; i < adapter->num_tx_queues; i++) { +		struct igc_ring *ring = adapter->tx_ring[i]; + +		ring->start_time = 0; +		ring->end_time = NSEC_PER_SEC; +	} + +	return 0; +} +  static int igc_save_qbv_schedule(struct igc_adapter *adapter,  				 struct tc_taprio_qopt_offload *qopt)  {  	u32 start_time = 0, end_time = 0;  	size_t n; -	if (!qopt->enable) { -		adapter->base_time = 0; -		return 0; -	} +	if (!qopt->enable) +		return igc_tsn_clear_schedule(adapter);  	if (adapter->base_time)  		return -EALREADY; @@ -5544,7 +5872,7 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter,  		end_time += e->interval; -		for (i = 0; i < IGC_MAX_TX_QUEUES; i++) { +		for (i = 0; i < adapter->num_tx_queues; i++) {  			struct igc_ring *ring = adapter->tx_ring[i];  			if (!(e->gate_mask & BIT(i))) @@ -5576,6 +5904,74 @@ static int igc_tsn_enable_qbv_scheduling(struct igc_adapter *adapter,  	return igc_tsn_offload_apply(adapter);  } +static int igc_save_cbs_params(struct igc_adapter *adapter, int queue, +			       bool enable, int idleslope, int sendslope, +			       int hicredit, int locredit) +{ +	bool cbs_status[IGC_MAX_SR_QUEUES] = { false }; +	struct net_device *netdev = adapter->netdev; +	struct igc_ring *ring; +	int i; + +	/* i225 has two sets of credit-based shaper logic. +	 * Supporting it only on the top two priority queues +	 */ +	if (queue < 0 || queue > 1) +		return -EINVAL; + +	ring = adapter->tx_ring[queue]; + +	for (i = 0; i < IGC_MAX_SR_QUEUES; i++) +		if (adapter->tx_ring[i]) +			cbs_status[i] = adapter->tx_ring[i]->cbs_enable; + +	/* CBS should be enabled on the highest priority queue first in order +	 * for the CBS algorithm to operate as intended. +	 */ +	if (enable) { +		if (queue == 1 && !cbs_status[0]) { +			netdev_err(netdev, +				   "Enabling CBS on queue1 before queue0\n"); +			return -EINVAL; +		} +	} else { +		if (queue == 0 && cbs_status[1]) { +			netdev_err(netdev, +				   "Disabling CBS on queue0 before queue1\n"); +			return -EINVAL; +		} +	} + +	ring->cbs_enable = enable; +	ring->idleslope = idleslope; +	ring->sendslope = sendslope; +	ring->hicredit = hicredit; +	ring->locredit = locredit; + +	return 0; +} + +static int igc_tsn_enable_cbs(struct igc_adapter *adapter, +			      struct tc_cbs_qopt_offload *qopt) +{ +	struct igc_hw *hw = &adapter->hw; +	int err; + +	if (hw->mac.type != igc_i225) +		return -EOPNOTSUPP; + +	if (qopt->queue < 0 || qopt->queue > 1) +		return -EINVAL; + +	err = igc_save_cbs_params(adapter, qopt->queue, qopt->enable, +				  qopt->idleslope, qopt->sendslope, +				  qopt->hicredit, qopt->locredit); +	if (err) +		return err; + +	return igc_tsn_offload_apply(adapter); +} +  static int igc_setup_tc(struct net_device *dev, enum tc_setup_type type,  			void *type_data)  { @@ -5588,6 +5984,9 @@ static int igc_setup_tc(struct net_device *dev, enum tc_setup_type type,  	case TC_SETUP_QDISC_ETF:  		return igc_tsn_enable_launchtime(adapter, type_data); +	case TC_SETUP_QDISC_CBS: +		return igc_tsn_enable_cbs(adapter, type_data); +  	default:  		return -EOPNOTSUPP;  	} @@ -5696,7 +6095,7 @@ static const struct net_device_ops igc_netdev_ops = {  	.ndo_fix_features	= igc_fix_features,  	.ndo_set_features	= igc_set_features,  	.ndo_features_check	= igc_features_check, -	.ndo_do_ioctl		= igc_ioctl, +	.ndo_eth_ioctl		= igc_ioctl,  	.ndo_setup_tc		= igc_setup_tc,  	.ndo_bpf		= igc_bpf,  	.ndo_xdp_xmit		= igc_xdp_xmit, @@ -5857,6 +6256,10 @@ static int igc_probe(struct pci_dev *pdev,  	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"); +  	pci_set_master(pdev);  	err = -ENOMEM; @@ -6010,6 +6413,8 @@ static int igc_probe(struct pci_dev *pdev,  	igc_ptp_init(adapter); +	igc_tsn_clear_schedule(adapter); +  	/* reset the hardware with the new settings */  	igc_reset(adapter); @@ -6054,6 +6459,7 @@ 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: diff --git a/drivers/net/ethernet/intel/igc/igc_phy.c b/drivers/net/ethernet/intel/igc/igc_phy.c index 83aeb5e7076f..5cad31c3c7b0 100644 --- a/drivers/net/ethernet/intel/igc/igc_phy.c +++ b/drivers/net/ethernet/intel/igc/igc_phy.c @@ -249,8 +249,7 @@ static s32 igc_phy_setup_autoneg(struct igc_hw *hw)  			return ret_val;  	} -	if ((phy->autoneg_mask & ADVERTISE_2500_FULL) && -	    hw->phy.id == I225_I_PHY_ID) { +	if (phy->autoneg_mask & ADVERTISE_2500_FULL) {  		/* Read the MULTI GBT AN Control Register - reg 7.32 */  		ret_val = phy->ops.read_reg(hw, (STANDARD_AN_REG_MASK <<  					    MMD_DEVADDR_SHIFT) | @@ -390,8 +389,7 @@ static s32 igc_phy_setup_autoneg(struct igc_hw *hw)  		ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL,  					     mii_1000t_ctrl_reg); -	if ((phy->autoneg_mask & ADVERTISE_2500_FULL) && -	    hw->phy.id == I225_I_PHY_ID) +	if (phy->autoneg_mask & ADVERTISE_2500_FULL)  		ret_val = phy->ops.write_reg(hw,  					     (STANDARD_AN_REG_MASK <<  					     MMD_DEVADDR_SHIFT) | diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c index 69617d2c1be2..0f021909b430 100644 --- a/drivers/net/ethernet/intel/igc/igc_ptp.c +++ b/drivers/net/ethernet/intel/igc/igc_ptp.c @@ -9,6 +9,8 @@  #include <linux/ptp_classify.h>  #include <linux/clocksource.h>  #include <linux/ktime.h> +#include <linux/delay.h> +#include <linux/iopoll.h>  #define INCVALUE_MASK		0x7fffffff  #define ISGN			0x80000000 @@ -16,6 +18,9 @@  #define IGC_SYSTIM_OVERFLOW_PERIOD	(HZ * 60 * 9)  #define IGC_PTP_TX_TIMEOUT		(HZ * 15) +#define IGC_PTM_STAT_SLEEP		2 +#define IGC_PTM_STAT_TIMEOUT		100 +  /* SYSTIM read access for I225 */  void igc_ptp_read(struct igc_adapter *adapter, struct timespec64 *ts)  { @@ -752,6 +757,147 @@ int igc_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr)  		-EFAULT : 0;  } +/* The two conditions below must be met for cross timestamping via + * PCIe PTM: + * + * 1. We have an way to convert the timestamps in the PTM messages + *    to something related to the system clocks (right now, only + *    X86 systems with support for the Always Running Timer allow that); + * + * 2. We have PTM enabled in the path from the device to the PCIe root port. + */ +static bool igc_is_crosststamp_supported(struct igc_adapter *adapter) +{ +	return IS_ENABLED(CONFIG_X86_TSC) ? pcie_ptm_enabled(adapter->pdev) : false; +} + +static struct system_counterval_t igc_device_tstamp_to_system(u64 tstamp) +{ +#if IS_ENABLED(CONFIG_X86_TSC) +	return convert_art_ns_to_tsc(tstamp); +#else +	return (struct system_counterval_t) { }; +#endif +} + +static void igc_ptm_log_error(struct igc_adapter *adapter, u32 ptm_stat) +{ +	struct net_device *netdev = adapter->netdev; + +	switch (ptm_stat) { +	case IGC_PTM_STAT_RET_ERR: +		netdev_err(netdev, "PTM Error: Root port timeout\n"); +		break; +	case IGC_PTM_STAT_BAD_PTM_RES: +		netdev_err(netdev, "PTM Error: Bad response, PTM Response Data expected\n"); +		break; +	case IGC_PTM_STAT_T4M1_OVFL: +		netdev_err(netdev, "PTM Error: T4 minus T1 overflow\n"); +		break; +	case IGC_PTM_STAT_ADJUST_1ST: +		netdev_err(netdev, "PTM Error: 1588 timer adjusted during first PTM cycle\n"); +		break; +	case IGC_PTM_STAT_ADJUST_CYC: +		netdev_err(netdev, "PTM Error: 1588 timer adjusted during non-first PTM cycle\n"); +		break; +	default: +		netdev_err(netdev, "PTM Error: Unknown error (%#x)\n", ptm_stat); +		break; +	} +} + +static int igc_phc_get_syncdevicetime(ktime_t *device, +				      struct system_counterval_t *system, +				      void *ctx) +{ +	u32 stat, t2_curr_h, t2_curr_l, ctrl; +	struct igc_adapter *adapter = ctx; +	struct igc_hw *hw = &adapter->hw; +	int err, count = 100; +	ktime_t t1, t2_curr; + +	/* Get a snapshot of system clocks to use as historic value. */ +	ktime_get_snapshot(&adapter->snapshot); + +	do { +		/* Doing this in a loop because in the event of a +		 * badly timed (ha!) system clock adjustment, we may +		 * get PTM errors from the PCI root, but these errors +		 * are transitory. Repeating the process returns valid +		 * data eventually. +		 */ + +		/* To "manually" start the PTM cycle we need to clear and +		 * then set again the TRIG bit. +		 */ +		ctrl = rd32(IGC_PTM_CTRL); +		ctrl &= ~IGC_PTM_CTRL_TRIG; +		wr32(IGC_PTM_CTRL, ctrl); +		ctrl |= IGC_PTM_CTRL_TRIG; +		wr32(IGC_PTM_CTRL, ctrl); + +		/* The cycle only starts "for real" when software notifies +		 * that it has read the registers, this is done by setting +		 * VALID bit. +		 */ +		wr32(IGC_PTM_STAT, IGC_PTM_STAT_VALID); + +		err = readx_poll_timeout(rd32, IGC_PTM_STAT, stat, +					 stat, IGC_PTM_STAT_SLEEP, +					 IGC_PTM_STAT_TIMEOUT); +		if (err < 0) { +			netdev_err(adapter->netdev, "Timeout reading IGC_PTM_STAT register\n"); +			return err; +		} + +		if ((stat & IGC_PTM_STAT_VALID) == IGC_PTM_STAT_VALID) +			break; + +		if (stat & ~IGC_PTM_STAT_VALID) { +			/* An error occurred, log it. */ +			igc_ptm_log_error(adapter, stat); +			/* The STAT register is write-1-to-clear (W1C), +			 * so write the previous error status to clear it. +			 */ +			wr32(IGC_PTM_STAT, stat); +			continue; +		} +	} while (--count); + +	if (!count) { +		netdev_err(adapter->netdev, "Exceeded number of tries for PTM cycle\n"); +		return -ETIMEDOUT; +	} + +	t1 = ktime_set(rd32(IGC_PTM_T1_TIM0_H), rd32(IGC_PTM_T1_TIM0_L)); + +	t2_curr_l = rd32(IGC_PTM_CURR_T2_L); +	t2_curr_h = rd32(IGC_PTM_CURR_T2_H); + +	/* FIXME: When the register that tells the endianness of the +	 * PTM registers are implemented, check them here and add the +	 * appropriate conversion. +	 */ +	t2_curr_h = swab32(t2_curr_h); + +	t2_curr = ((s64)t2_curr_h << 32 | t2_curr_l); + +	*device = t1; +	*system = igc_device_tstamp_to_system(t2_curr); + +	return 0; +} + +static int igc_ptp_getcrosststamp(struct ptp_clock_info *ptp, +				  struct system_device_crosststamp *cts) +{ +	struct igc_adapter *adapter = container_of(ptp, struct igc_adapter, +						   ptp_caps); + +	return get_device_system_crosststamp(igc_phc_get_syncdevicetime, +					     adapter, &adapter->snapshot, cts); +} +  /**   * igc_ptp_init - Initialize PTP functionality   * @adapter: Board private structure @@ -788,6 +934,11 @@ void igc_ptp_init(struct igc_adapter *adapter)  		adapter->ptp_caps.n_per_out = IGC_N_PEROUT;  		adapter->ptp_caps.n_pins = IGC_N_SDP;  		adapter->ptp_caps.verify = igc_ptp_verify_pin; + +		if (!igc_is_crosststamp_supported(adapter)) +			break; + +		adapter->ptp_caps.getcrosststamp = igc_ptp_getcrosststamp;  		break;  	default:  		adapter->ptp_clock = NULL; @@ -849,7 +1000,8 @@ void igc_ptp_suspend(struct igc_adapter *adapter)  	adapter->ptp_tx_skb = NULL;  	clear_bit_unlock(__IGC_PTP_TX_IN_PROGRESS, &adapter->state); -	igc_ptp_time_save(adapter); +	if (pci_device_is_present(adapter->pdev)) +		igc_ptp_time_save(adapter);  }  /** @@ -878,7 +1030,9 @@ void igc_ptp_stop(struct igc_adapter *adapter)  void igc_ptp_reset(struct igc_adapter *adapter)  {  	struct igc_hw *hw = &adapter->hw; +	u32 cycle_ctrl, ctrl;  	unsigned long flags; +	u32 timadj;  	/* reset the tstamp_config */  	igc_ptp_set_timestamp_mode(adapter, &adapter->tstamp_config); @@ -887,12 +1041,38 @@ void igc_ptp_reset(struct igc_adapter *adapter)  	switch (adapter->hw.mac.type) {  	case igc_i225: +		timadj = rd32(IGC_TIMADJ); +		timadj |= IGC_TIMADJ_ADJUST_METH; +		wr32(IGC_TIMADJ, timadj); +  		wr32(IGC_TSAUXC, 0x0);  		wr32(IGC_TSSDP, 0x0);  		wr32(IGC_TSIM,  		     IGC_TSICR_INTERRUPTS |  		     (adapter->pps_sys_wrap_on ? IGC_TSICR_SYS_WRAP : 0));  		wr32(IGC_IMS, IGC_IMS_TS); + +		if (!igc_is_crosststamp_supported(adapter)) +			break; + +		wr32(IGC_PCIE_DIG_DELAY, IGC_PCIE_DIG_DELAY_DEFAULT); +		wr32(IGC_PCIE_PHY_DELAY, IGC_PCIE_PHY_DELAY_DEFAULT); + +		cycle_ctrl = IGC_PTM_CYCLE_CTRL_CYC_TIME(IGC_PTM_CYC_TIME_DEFAULT); + +		wr32(IGC_PTM_CYCLE_CTRL, cycle_ctrl); + +		ctrl = IGC_PTM_CTRL_EN | +			IGC_PTM_CTRL_START_NOW | +			IGC_PTM_CTRL_SHRT_CYC(IGC_PTM_SHORT_CYC_DEFAULT) | +			IGC_PTM_CTRL_PTM_TO(IGC_PTM_TIMEOUT_DEFAULT) | +			IGC_PTM_CTRL_TRIG; + +		wr32(IGC_PTM_CTRL, ctrl); + +		/* Force the first cycle to run. */ +		wr32(IGC_PTM_STAT, IGC_PTM_STAT_VALID); +  		break;  	default:  		/* No work to do. */ diff --git a/drivers/net/ethernet/intel/igc/igc_regs.h b/drivers/net/ethernet/intel/igc/igc_regs.h index 0f82990567d9..e197a33d93a0 100644 --- a/drivers/net/ethernet/intel/igc/igc_regs.h +++ b/drivers/net/ethernet/intel/igc/igc_regs.h @@ -67,6 +67,9 @@  /* Filtering Registers */  #define IGC_ETQF(_n)		(0x05CB0 + (4 * (_n))) /* EType Queue Fltr */ +#define IGC_FHFT(_n)		(0x09000 + (256 * (_n))) /* Flexible Host Filter */ +#define IGC_FHFT_EXT(_n)	(0x09A00 + (256 * (_n))) /* Flexible Host Filter Extended */ +#define IGC_FHFTSL		0x05804 /* Flex Filter indirect table select */  /* ETQF register bit definitions */  #define IGC_ETQF_FILTER_ENABLE	BIT(26) @@ -75,6 +78,19 @@  #define IGC_ETQF_QUEUE_MASK	0x00070000  #define IGC_ETQF_ETYPE_MASK	0x0000FFFF +/* FHFT register bit definitions */ +#define IGC_FHFT_LENGTH_MASK	GENMASK(7, 0) +#define IGC_FHFT_QUEUE_SHIFT	8 +#define IGC_FHFT_QUEUE_MASK	GENMASK(10, 8) +#define IGC_FHFT_PRIO_SHIFT	16 +#define IGC_FHFT_PRIO_MASK	GENMASK(18, 16) +#define IGC_FHFT_IMM_INT	BIT(24) +#define IGC_FHFT_DROP		BIT(25) + +/* FHFTSL register bit definitions */ +#define IGC_FHFTSL_FTSL_SHIFT	0 +#define IGC_FHFTSL_FTSL_MASK	GENMASK(1, 0) +  /* Redirection Table - RW Array */  #define IGC_RETA(_i)		(0x05C00 + ((_i) * 4))  /* RSS Random Key - RW Array */ @@ -220,6 +236,9 @@  #define IGC_ENDQT(_n)		(0x3334 + 0x4 * (_n))  #define IGC_DTXMXPKTSZ		0x355C +#define IGC_TQAVCC(_n)		(0x3004 + ((_n) * 0x40)) +#define IGC_TQAVHC(_n)		(0x300C + ((_n) * 0x40)) +  /* System Time Registers */  #define IGC_SYSTIML	0x0B600  /* System time register Low - RO */  #define IGC_SYSTIMH	0x0B604  /* System time register High - RO */ @@ -229,6 +248,29 @@  #define IGC_TXSTMPL	0x0B618  /* Tx timestamp value Low - RO */  #define IGC_TXSTMPH	0x0B61C  /* Tx timestamp value High - RO */ +#define IGC_TIMADJ	0x0B60C  /* Time Adjustment Offset Register */ + +/* PCIe Registers */ +#define IGC_PTM_CTRL		0x12540  /* PTM Control */ +#define IGC_PTM_STAT		0x12544  /* PTM Status */ +#define IGC_PTM_CYCLE_CTRL	0x1254C  /* PTM Cycle Control */ + +/* PTM Time registers */ +#define IGC_PTM_T1_TIM0_L	0x12558  /* T1 on Timer 0 Low */ +#define IGC_PTM_T1_TIM0_H	0x1255C  /* T1 on Timer 0 High */ + +#define IGC_PTM_CURR_T2_L	0x1258C  /* Current T2 Low */ +#define IGC_PTM_CURR_T2_H	0x12590  /* Current T2 High */ +#define IGC_PTM_PREV_T2_L	0x12584  /* Previous T2 Low */ +#define IGC_PTM_PREV_T2_H	0x12588  /* Previous T2 High */ +#define IGC_PTM_PREV_T4M1	0x12578  /* T4 Minus T1 on previous PTM Cycle */ +#define IGC_PTM_CURR_T4M1	0x1257C  /* T4 Minus T1 on this PTM Cycle */ +#define IGC_PTM_PREV_T3M2	0x12580  /* T3 Minus T2 on previous PTM Cycle */ +#define IGC_PTM_TDELAY		0x12594  /* PTM PCIe Link Delay */ + +#define IGC_PCIE_DIG_DELAY	0x12550  /* PCIe Digital Delay */ +#define IGC_PCIE_PHY_DELAY	0x12554  /* PCIe PHY Delay */ +  /* Management registers */  #define IGC_MANC	0x05820  /* Management Control - RW */ @@ -240,6 +282,7 @@  #define IGC_WUFC	0x05808  /* Wakeup Filter Control - RW */  #define IGC_WUS		0x05810  /* Wakeup Status - R/W1C */  #define IGC_WUPL	0x05900  /* Wakeup Packet Length - RW */ +#define IGC_WUFC_EXT	0x0580C  /* Wakeup Filter Control Register Extended - RW */  /* Wake Up packet memory */  #define IGC_WUPM_REG(_i)	(0x05A00 + ((_i) * 4)) diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.c b/drivers/net/ethernet/intel/igc/igc_tsn.c index 174103c4bea6..0fce22de2ab8 100644 --- a/drivers/net/ethernet/intel/igc/igc_tsn.c +++ b/drivers/net/ethernet/intel/igc/igc_tsn.c @@ -18,8 +18,38 @@ static bool is_any_launchtime(struct igc_adapter *adapter)  	return false;  } +static bool is_cbs_enabled(struct igc_adapter *adapter) +{ +	int i; + +	for (i = 0; i < adapter->num_tx_queues; i++) { +		struct igc_ring *ring = adapter->tx_ring[i]; + +		if (ring->cbs_enable) +			return true; +	} + +	return false; +} + +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) +		new_flags |= IGC_FLAG_TSN_QBV_ENABLED; + +	if (is_any_launchtime(adapter)) +		new_flags |= IGC_FLAG_TSN_QBV_ENABLED; + +	if (is_cbs_enabled(adapter)) +		new_flags |= IGC_FLAG_TSN_QAV_ENABLED; + +	return new_flags; +} +  /* Returns the TSN specific registers to their default values after - * TSN offloading is disabled. + * the adapter is reset.   */  static int igc_tsn_disable_offload(struct igc_adapter *adapter)  { @@ -27,11 +57,6 @@ static int igc_tsn_disable_offload(struct igc_adapter *adapter)  	u32 tqavctrl;  	int i; -	if (!(adapter->flags & IGC_FLAG_TSN_QBV_ENABLED)) -		return 0; - -	adapter->cycle_time = 0; -  	wr32(IGC_TXPBS, I225_TXPBSIZE_DEFAULT);  	wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_DEFAULT); @@ -41,18 +66,12 @@ static int igc_tsn_disable_offload(struct igc_adapter *adapter)  	wr32(IGC_TQAVCTRL, tqavctrl);  	for (i = 0; i < adapter->num_tx_queues; i++) { -		struct igc_ring *ring = adapter->tx_ring[i]; - -		ring->start_time = 0; -		ring->end_time = 0; -		ring->launchtime_enable = false; -  		wr32(IGC_TXQCTL(i), 0);  		wr32(IGC_STQT(i), 0);  		wr32(IGC_ENDQT(i), NSEC_PER_SEC);  	} -	wr32(IGC_QBVCYCLET_S, NSEC_PER_SEC); +	wr32(IGC_QBVCYCLET_S, 0);  	wr32(IGC_QBVCYCLET, NSEC_PER_SEC);  	adapter->flags &= ~IGC_FLAG_TSN_QBV_ENABLED; @@ -68,9 +87,6 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter)  	ktime_t base_time, systim;  	int i; -	if (adapter->flags & IGC_FLAG_TSN_QBV_ENABLED) -		return 0; -  	cycle = adapter->cycle_time;  	base_time = adapter->base_time; @@ -88,6 +104,8 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter)  	for (i = 0; i < adapter->num_tx_queues; i++) {  		struct igc_ring *ring = adapter->tx_ring[i];  		u32 txqctl = 0; +		u16 cbs_value; +		u32 tqavcc;  		wr32(IGC_STQT(i), ring->start_time);  		wr32(IGC_ENDQT(i), ring->end_time); @@ -105,6 +123,90 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter)  		if (ring->launchtime_enable)  			txqctl |= IGC_TXQCTL_QUEUE_MODE_LAUNCHT; +		/* Skip configuring CBS for Q2 and Q3 */ +		if (i > 1) +			goto skip_cbs; + +		if (ring->cbs_enable) { +			if (i == 0) +				txqctl |= IGC_TXQCTL_QAV_SEL_CBS0; +			else +				txqctl |= IGC_TXQCTL_QAV_SEL_CBS1; + +			/* According to i225 datasheet section 7.5.2.7, we +			 * should set the 'idleSlope' field from TQAVCC +			 * register following the equation: +			 * +			 * value = link-speed   0x7736 * BW * 0.2 +			 *         ---------- *  -----------------         (E1) +			 *          100Mbps            2.5 +			 * +			 * Note that 'link-speed' is in Mbps. +			 * +			 * 'BW' is the percentage bandwidth out of full +			 * link speed which can be found with the +			 * following equation. Note that idleSlope here +			 * is the parameter from this function +			 * which is in kbps. +			 * +			 *     BW =     idleSlope +			 *          -----------------                      (E2) +			 *          link-speed * 1000 +			 * +			 * That said, we can come up with a generic +			 * equation to calculate the value we should set +			 * it TQAVCC register by replacing 'BW' in E1 by E2. +			 * The resulting equation is: +			 * +			 * value = link-speed * 0x7736 * idleSlope * 0.2 +			 *         -------------------------------------   (E3) +			 *             100 * 2.5 * link-speed * 1000 +			 * +			 * 'link-speed' is present in both sides of the +			 * fraction so it is canceled out. The final +			 * equation is the following: +			 * +			 *     value = idleSlope * 61036 +			 *             -----------------                   (E4) +			 *                  2500000 +			 * +			 * NOTE: For i225, given the above, we can see +			 *       that idleslope is represented in +			 *       40.959433 kbps units by the value at +			 *       the TQAVCC register (2.5Gbps / 61036), +			 *       which reduces the granularity for +			 *       idleslope increments. +			 * +			 * In i225 controller, the sendSlope and loCredit +			 * parameters from CBS are not configurable +			 * by software so we don't do any +			 * 'controller configuration' in respect to +			 * these parameters. +			 */ +			cbs_value = DIV_ROUND_UP_ULL(ring->idleslope +						     * 61036ULL, 2500000); + +			tqavcc = rd32(IGC_TQAVCC(i)); +			tqavcc &= ~IGC_TQAVCC_IDLESLOPE_MASK; +			tqavcc |= cbs_value | IGC_TQAVCC_KEEP_CREDITS; +			wr32(IGC_TQAVCC(i), tqavcc); + +			wr32(IGC_TQAVHC(i), +			     0x80000000 + ring->hicredit * 0x7735); +		} else { +			/* Disable any CBS for the queue */ +			txqctl &= ~(IGC_TXQCTL_QAV_SEL_MASK); + +			/* Set idleSlope to zero. */ +			tqavcc = rd32(IGC_TQAVCC(i)); +			tqavcc &= ~(IGC_TQAVCC_IDLESLOPE_MASK | +				    IGC_TQAVCC_KEEP_CREDITS); +			wr32(IGC_TQAVCC(i), tqavcc); + +			/* Set hiCredit to zero. */ +			wr32(IGC_TQAVHC(i), 0); +		} +skip_cbs:  		wr32(IGC_TXQCTL(i), txqctl);  	} @@ -125,33 +227,41 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter)  	wr32(IGC_BASET_H, baset_h);  	wr32(IGC_BASET_L, baset_l); -	adapter->flags |= IGC_FLAG_TSN_QBV_ENABLED; -  	return 0;  } -int igc_tsn_offload_apply(struct igc_adapter *adapter) +int igc_tsn_reset(struct igc_adapter *adapter)  { -	bool is_any_enabled = adapter->base_time || is_any_launchtime(adapter); +	unsigned int new_flags; +	int err = 0; -	if (!(adapter->flags & IGC_FLAG_TSN_QBV_ENABLED) && !is_any_enabled) -		return 0; +	new_flags = igc_tsn_new_flags(adapter); -	if (!is_any_enabled) { -		int err = igc_tsn_disable_offload(adapter); +	if (!(new_flags & IGC_FLAG_TSN_ANY_ENABLED)) +		return igc_tsn_disable_offload(adapter); -		if (err < 0) -			return err; +	err = igc_tsn_enable_offload(adapter); +	if (err < 0) +		return err; -		/* The BASET registers aren't cleared when writing -		 * into them, force a reset if the interface is -		 * running. -		 */ -		if (netif_running(adapter->netdev)) -			schedule_work(&adapter->reset_task); +	adapter->flags = new_flags; +	return err; +} + +int igc_tsn_offload_apply(struct igc_adapter *adapter) +{ +	int err; + +	if (netif_running(adapter->netdev)) { +		schedule_work(&adapter->reset_task);  		return 0;  	} -	return igc_tsn_enable_offload(adapter); +	err = igc_tsn_enable_offload(adapter); +	if (err < 0) +		return err; + +	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 f76bc86ddccd..1512307f5a52 100644 --- a/drivers/net/ethernet/intel/igc/igc_tsn.h +++ b/drivers/net/ethernet/intel/igc/igc_tsn.h @@ -5,5 +5,6 @@  #define _IGC_TSN_H_  int igc_tsn_offload_apply(struct igc_adapter *adapter); +int igc_tsn_reset(struct igc_adapter *adapter);  #endif /* _IGC_BASE_H */ |