diff options
| -rw-r--r-- | drivers/net/ethernet/intel/i40e/Makefile | 1 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e.h | 29 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_adminq.c | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h | 4 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_common.c | 286 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_dcb.c | 28 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_dcb.h | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_ddp.c | 481 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 8 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_main.c | 58 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_prototype.h | 6 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_ptp.c | 58 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_type.h | 23 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 6 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/iavf/i40e_adminq_cmd.h | 2 | 
15 files changed, 901 insertions, 93 deletions
| diff --git a/drivers/net/ethernet/intel/i40e/Makefile b/drivers/net/ethernet/intel/i40e/Makefile index 50590e8d1fd1..2f21b3e89fd0 100644 --- a/drivers/net/ethernet/intel/i40e/Makefile +++ b/drivers/net/ethernet/intel/i40e/Makefile @@ -21,6 +21,7 @@ i40e-objs := i40e_main.o \  	i40e_diag.o	\  	i40e_txrx.o	\  	i40e_ptp.o	\ +	i40e_ddp.o \  	i40e_client.o   \  	i40e_virtchnl_pf.o \  	i40e_xsk.o diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index d3cc3427caad..c4afb852cb57 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -321,6 +321,29 @@ struct i40e_udp_port_config {  	u8 filter_index;  }; +#define I40_DDP_FLASH_REGION 100 +#define I40E_PROFILE_INFO_SIZE 48 +#define I40E_MAX_PROFILE_NUM 16 +#define I40E_PROFILE_LIST_SIZE \ +	(I40E_PROFILE_INFO_SIZE * I40E_MAX_PROFILE_NUM + 4) +#define I40E_DDP_PROFILE_PATH "intel/i40e/ddp/" +#define I40E_DDP_PROFILE_NAME_MAX 64 + +int i40e_ddp_load(struct net_device *netdev, const u8 *data, size_t size, +		  bool is_add); +int i40e_ddp_flash(struct net_device *netdev, struct ethtool_flash *flash); + +struct i40e_ddp_profile_list { +	u32 p_count; +	struct i40e_profile_info p_info[0]; +}; + +struct i40e_ddp_old_profile_list { +	struct list_head list; +	size_t old_ddp_size; +	u8 old_ddp_buf[0]; +}; +  /* macros related to FLX_PIT */  #define I40E_FLEX_SET_FSIZE(fsize) (((fsize) << \  				    I40E_PRTQF_FLX_PIT_FSIZE_SHIFT) & \ @@ -589,6 +612,8 @@ struct i40e_pf {  	struct sk_buff *ptp_tx_skb;  	unsigned long ptp_tx_start;  	struct hwtstamp_config tstamp_config; +	struct timespec64 ptp_prev_hw_time; +	ktime_t ptp_reset_start;  	struct mutex tmreg_lock; /* Used to protect the SYSTIME registers. */  	u32 ptp_adj_mult;  	u32 tx_hwtstamp_timeouts; @@ -610,6 +635,8 @@ struct i40e_pf {  	u16 override_q_count;  	u16 last_sw_conf_flags;  	u16 last_sw_conf_valid_flags; +	/* List to keep previous DDP profiles to be rolled back in the future */ +	struct list_head ddp_old_prof;  };  /** @@ -1083,6 +1110,8 @@ void i40e_ptp_rx_hwtstamp(struct i40e_pf *pf, struct sk_buff *skb, u8 index);  void i40e_ptp_set_increment(struct i40e_pf *pf);  int i40e_ptp_set_ts_config(struct i40e_pf *pf, struct ifreq *ifr);  int i40e_ptp_get_ts_config(struct i40e_pf *pf, struct ifreq *ifr); +void i40e_ptp_save_hw_time(struct i40e_pf *pf); +void i40e_ptp_restore_hw_time(struct i40e_pf *pf);  void i40e_ptp_init(struct i40e_pf *pf);  void i40e_ptp_stop(struct i40e_pf *pf);  int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi); diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.c b/drivers/net/ethernet/intel/i40e/i40e_adminq.c index 7ab61f6ebb5f..45f6adc8ff2f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.c @@ -749,7 +749,7 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,  	if (val >= hw->aq.num_asq_entries) {  		i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE,  			   "AQTX: head overrun at %d\n", val); -		status = I40E_ERR_QUEUE_EMPTY; +		status = I40E_ERR_ADMIN_QUEUE_FULL;  		goto asq_send_command_error;  	} diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h index 11506102471c..522058a7d4be 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h @@ -11,8 +11,8 @@   */  #define I40E_FW_API_VERSION_MAJOR	0x0001 -#define I40E_FW_API_VERSION_MINOR_X722	0x0006 -#define I40E_FW_API_VERSION_MINOR_X710	0x0007 +#define I40E_FW_API_VERSION_MINOR_X722	0x0008 +#define I40E_FW_API_VERSION_MINOR_X710	0x0008  #define I40E_FW_MINOR_VERSION(_h) ((_h)->mac.type == I40E_MAC_XL710 ? \  					I40E_FW_API_VERSION_MINOR_X710 : \ diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 97a9b1fb4763..dd6b3b3ac5c6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -1466,7 +1466,6 @@ static u32 i40e_led_is_mine(struct i40e_hw *hw, int idx)   **/  u32 i40e_led_get(struct i40e_hw *hw)  { -	u32 current_mode = 0;  	u32 mode = 0;  	int i; @@ -1479,21 +1478,6 @@ u32 i40e_led_get(struct i40e_hw *hw)  		if (!gpio_val)  			continue; -		/* ignore gpio LED src mode entries related to the activity -		 * LEDs -		 */ -		current_mode = ((gpio_val & I40E_GLGEN_GPIO_CTL_LED_MODE_MASK) -				>> I40E_GLGEN_GPIO_CTL_LED_MODE_SHIFT); -		switch (current_mode) { -		case I40E_COMBINED_ACTIVITY: -		case I40E_FILTER_ACTIVITY: -		case I40E_MAC_ACTIVITY: -		case I40E_LINK_ACTIVITY: -			continue; -		default: -			break; -		} -  		mode = (gpio_val & I40E_GLGEN_GPIO_CTL_LED_MODE_MASK) >>  			I40E_GLGEN_GPIO_CTL_LED_MODE_SHIFT;  		break; @@ -1513,7 +1497,6 @@ u32 i40e_led_get(struct i40e_hw *hw)   **/  void i40e_led_set(struct i40e_hw *hw, u32 mode, bool blink)  { -	u32 current_mode = 0;  	int i;  	if (mode & 0xfffffff0) @@ -1527,22 +1510,6 @@ void i40e_led_set(struct i40e_hw *hw, u32 mode, bool blink)  		if (!gpio_val)  			continue; - -		/* ignore gpio LED src mode entries related to the activity -		 * LEDs -		 */ -		current_mode = ((gpio_val & I40E_GLGEN_GPIO_CTL_LED_MODE_MASK) -				>> I40E_GLGEN_GPIO_CTL_LED_MODE_SHIFT); -		switch (current_mode) { -		case I40E_COMBINED_ACTIVITY: -		case I40E_FILTER_ACTIVITY: -		case I40E_MAC_ACTIVITY: -		case I40E_LINK_ACTIVITY: -			continue; -		default: -			break; -		} -  		gpio_val &= ~I40E_GLGEN_GPIO_CTL_LED_MODE_MASK;  		/* this & is a bit of paranoia, but serves as a range check */  		gpio_val |= ((mode << I40E_GLGEN_GPIO_CTL_LED_MODE_SHIFT) & @@ -5448,6 +5415,163 @@ i40e_find_segment_in_package(u32 segment_type,  	return NULL;  } +/* Get section table in profile */ +#define I40E_SECTION_TABLE(profile, sec_tbl)				\ +	do {								\ +		struct i40e_profile_segment *p = (profile);		\ +		u32 count;						\ +		u32 *nvm;						\ +		count = p->device_table_count;				\ +		nvm = (u32 *)&p->device_table[count];			\ +		sec_tbl = (struct i40e_section_table *)&nvm[nvm[0] + 1]; \ +	} while (0) + +/* Get section header in profile */ +#define I40E_SECTION_HEADER(profile, offset)				\ +	(struct i40e_profile_section_header *)((u8 *)(profile) + (offset)) + +/** + * i40e_find_section_in_profile + * @section_type: the section type to search for (i.e., SECTION_TYPE_NOTE) + * @profile: pointer to the i40e segment header to be searched + * + * This function searches i40e segment for a particular section type. On + * success it returns a pointer to the section header, otherwise it will + * return NULL. + **/ +struct i40e_profile_section_header * +i40e_find_section_in_profile(u32 section_type, +			     struct i40e_profile_segment *profile) +{ +	struct i40e_profile_section_header *sec; +	struct i40e_section_table *sec_tbl; +	u32 sec_off; +	u32 i; + +	if (profile->header.type != SEGMENT_TYPE_I40E) +		return NULL; + +	I40E_SECTION_TABLE(profile, sec_tbl); + +	for (i = 0; i < sec_tbl->section_count; i++) { +		sec_off = sec_tbl->section_offset[i]; +		sec = I40E_SECTION_HEADER(profile, sec_off); +		if (sec->section.type == section_type) +			return sec; +	} + +	return NULL; +} + +/** + * i40e_ddp_exec_aq_section - Execute generic AQ for DDP + * @hw: pointer to the hw struct + * @aq: command buffer containing all data to execute AQ + **/ +static enum +i40e_status_code i40e_ddp_exec_aq_section(struct i40e_hw *hw, +					  struct i40e_profile_aq_section *aq) +{ +	i40e_status status; +	struct i40e_aq_desc desc; +	u8 *msg = NULL; +	u16 msglen; + +	i40e_fill_default_direct_cmd_desc(&desc, aq->opcode); +	desc.flags |= cpu_to_le16(aq->flags); +	memcpy(desc.params.raw, aq->param, sizeof(desc.params.raw)); + +	msglen = aq->datalen; +	if (msglen) { +		desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | +						I40E_AQ_FLAG_RD)); +		if (msglen > I40E_AQ_LARGE_BUF) +			desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); +		desc.datalen = cpu_to_le16(msglen); +		msg = &aq->data[0]; +	} + +	status = i40e_asq_send_command(hw, &desc, msg, msglen, NULL); + +	if (status) { +		i40e_debug(hw, I40E_DEBUG_PACKAGE, +			   "unable to exec DDP AQ opcode %u, error %d\n", +			   aq->opcode, status); +		return status; +	} + +	/* copy returned desc to aq_buf */ +	memcpy(aq->param, desc.params.raw, sizeof(desc.params.raw)); + +	return 0; +} + +/** + * i40e_validate_profile + * @hw: pointer to the hardware structure + * @profile: pointer to the profile segment of the package to be validated + * @track_id: package tracking id + * @rollback: flag if the profile is for rollback. + * + * Validates supported devices and profile's sections. + */ +static enum i40e_status_code +i40e_validate_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile, +		      u32 track_id, bool rollback) +{ +	struct i40e_profile_section_header *sec = NULL; +	i40e_status status = 0; +	struct i40e_section_table *sec_tbl; +	u32 vendor_dev_id; +	u32 dev_cnt; +	u32 sec_off; +	u32 i; + +	if (track_id == I40E_DDP_TRACKID_INVALID) { +		i40e_debug(hw, I40E_DEBUG_PACKAGE, "Invalid track_id\n"); +		return I40E_NOT_SUPPORTED; +	} + +	dev_cnt = profile->device_table_count; +	for (i = 0; i < dev_cnt; i++) { +		vendor_dev_id = profile->device_table[i].vendor_dev_id; +		if ((vendor_dev_id >> 16) == PCI_VENDOR_ID_INTEL && +		    hw->device_id == (vendor_dev_id & 0xFFFF)) +			break; +	} +	if (dev_cnt && i == dev_cnt) { +		i40e_debug(hw, I40E_DEBUG_PACKAGE, +			   "Device doesn't support DDP\n"); +		return I40E_ERR_DEVICE_NOT_SUPPORTED; +	} + +	I40E_SECTION_TABLE(profile, sec_tbl); + +	/* Validate sections types */ +	for (i = 0; i < sec_tbl->section_count; i++) { +		sec_off = sec_tbl->section_offset[i]; +		sec = I40E_SECTION_HEADER(profile, sec_off); +		if (rollback) { +			if (sec->section.type == SECTION_TYPE_MMIO || +			    sec->section.type == SECTION_TYPE_AQ || +			    sec->section.type == SECTION_TYPE_RB_AQ) { +				i40e_debug(hw, I40E_DEBUG_PACKAGE, +					   "Not a roll-back package\n"); +				return I40E_NOT_SUPPORTED; +			} +		} else { +			if (sec->section.type == SECTION_TYPE_RB_AQ || +			    sec->section.type == SECTION_TYPE_RB_MMIO) { +				i40e_debug(hw, I40E_DEBUG_PACKAGE, +					   "Not an original package\n"); +				return I40E_NOT_SUPPORTED; +			} +		} +	} + +	return status; +} +  /**   * i40e_write_profile   * @hw: pointer to the hardware structure @@ -5463,47 +5587,99 @@ i40e_write_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile,  	i40e_status status = 0;  	struct i40e_section_table *sec_tbl;  	struct i40e_profile_section_header *sec = NULL; -	u32 dev_cnt; -	u32 vendor_dev_id; -	u32 *nvm; +	struct i40e_profile_aq_section *ddp_aq;  	u32 section_size = 0;  	u32 offset = 0, info = 0; +	u32 sec_off;  	u32 i; -	dev_cnt = profile->device_table_count; +	status = i40e_validate_profile(hw, profile, track_id, false); +	if (status) +		return status; -	for (i = 0; i < dev_cnt; i++) { -		vendor_dev_id = profile->device_table[i].vendor_dev_id; -		if ((vendor_dev_id >> 16) == PCI_VENDOR_ID_INTEL) -			if (hw->device_id == (vendor_dev_id & 0xFFFF)) +	I40E_SECTION_TABLE(profile, sec_tbl); + +	for (i = 0; i < sec_tbl->section_count; i++) { +		sec_off = sec_tbl->section_offset[i]; +		sec = I40E_SECTION_HEADER(profile, sec_off); +		/* Process generic admin command */ +		if (sec->section.type == SECTION_TYPE_AQ) { +			ddp_aq = (struct i40e_profile_aq_section *)&sec[1]; +			status = i40e_ddp_exec_aq_section(hw, ddp_aq); +			if (status) { +				i40e_debug(hw, I40E_DEBUG_PACKAGE, +					   "Failed to execute aq: section %d, opcode %u\n", +					   i, ddp_aq->opcode);  				break; +			} +			sec->section.type = SECTION_TYPE_RB_AQ; +		} + +		/* Skip any non-mmio sections */ +		if (sec->section.type != SECTION_TYPE_MMIO) +			continue; + +		section_size = sec->section.size + +			sizeof(struct i40e_profile_section_header); + +		/* Write MMIO section */ +		status = i40e_aq_write_ddp(hw, (void *)sec, (u16)section_size, +					   track_id, &offset, &info, NULL); +		if (status) { +			i40e_debug(hw, I40E_DEBUG_PACKAGE, +				   "Failed to write profile: section %d, offset %d, info %d\n", +				   i, offset, info); +			break; +		}  	} -	if (i == dev_cnt) { -		i40e_debug(hw, I40E_DEBUG_PACKAGE, "Device doesn't support DDP"); -		return I40E_ERR_DEVICE_NOT_SUPPORTED; -	} +	return status; +} + +/** + * i40e_rollback_profile + * @hw: pointer to the hardware structure + * @profile: pointer to the profile segment of the package to be removed + * @track_id: package tracking id + * + * Rolls back previously loaded package. + */ +enum i40e_status_code +i40e_rollback_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile, +		      u32 track_id) +{ +	struct i40e_profile_section_header *sec = NULL; +	i40e_status status = 0; +	struct i40e_section_table *sec_tbl; +	u32 offset = 0, info = 0; +	u32 section_size = 0; +	u32 sec_off; +	int i; + +	status = i40e_validate_profile(hw, profile, track_id, true); +	if (status) +		return status; -	nvm = (u32 *)&profile->device_table[dev_cnt]; -	sec_tbl = (struct i40e_section_table *)&nvm[nvm[0] + 1]; +	I40E_SECTION_TABLE(profile, sec_tbl); -	for (i = 0; i < sec_tbl->section_count; i++) { -		sec = (struct i40e_profile_section_header *)((u8 *)profile + -					     sec_tbl->section_offset[i]); +	/* For rollback write sections in reverse */ +	for (i = sec_tbl->section_count - 1; i >= 0; i--) { +		sec_off = sec_tbl->section_offset[i]; +		sec = I40E_SECTION_HEADER(profile, sec_off); -		/* Skip 'AQ', 'note' and 'name' sections */ -		if (sec->section.type != SECTION_TYPE_MMIO) +		/* Skip any non-rollback sections */ +		if (sec->section.type != SECTION_TYPE_RB_MMIO)  			continue;  		section_size = sec->section.size +  			sizeof(struct i40e_profile_section_header); -		/* Write profile */ +		/* Write roll-back MMIO section */  		status = i40e_aq_write_ddp(hw, (void *)sec, (u16)section_size,  					   track_id, &offset, &info, NULL);  		if (status) {  			i40e_debug(hw, I40E_DEBUG_PACKAGE, -				   "Failed to write profile: offset %d, info %d", -				   offset, info); +				   "Failed to write profile: section %d, offset %d, info %d\n", +				   i, offset, info);  			break;  		}  	} diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.c b/drivers/net/ethernet/intel/i40e/i40e_dcb.c index 56bff8faf371..292eeb3def10 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.c @@ -863,22 +863,23 @@ out:  /**   * i40e_init_dcb   * @hw: pointer to the hw struct + * @enable_mib_change: enable mib change event   *   * Update DCB configuration from the Firmware   **/ -i40e_status i40e_init_dcb(struct i40e_hw *hw) +i40e_status i40e_init_dcb(struct i40e_hw *hw, bool enable_mib_change)  {  	i40e_status ret = 0;  	struct i40e_lldp_variables lldp_cfg;  	u8 adminstatus = 0;  	if (!hw->func_caps.dcb) -		return ret; +		return I40E_NOT_SUPPORTED;  	/* Read LLDP NVM area */  	ret = i40e_read_lldp_cfg(hw, &lldp_cfg);  	if (ret) -		return ret; +		return I40E_ERR_NOT_READY;  	/* Get the LLDP AdminStatus for the current port */  	adminstatus = lldp_cfg.adminstatus >> (hw->port * 4); @@ -887,7 +888,7 @@ i40e_status i40e_init_dcb(struct i40e_hw *hw)  	/* LLDP agent disabled */  	if (!adminstatus) {  		hw->dcbx_status = I40E_DCBX_STATUS_DISABLED; -		return ret; +		return I40E_ERR_NOT_READY;  	}  	/* Get DCBX status */ @@ -896,26 +897,19 @@ i40e_status i40e_init_dcb(struct i40e_hw *hw)  		return ret;  	/* Check the DCBX Status */ -	switch (hw->dcbx_status) { -	case I40E_DCBX_STATUS_DONE: -	case I40E_DCBX_STATUS_IN_PROGRESS: +	if (hw->dcbx_status == I40E_DCBX_STATUS_DONE || +	    hw->dcbx_status == I40E_DCBX_STATUS_IN_PROGRESS) {  		/* Get current DCBX configuration */  		ret = i40e_get_dcb_config(hw);  		if (ret)  			return ret; -		break; -	case I40E_DCBX_STATUS_DISABLED: -		return ret; -	case I40E_DCBX_STATUS_NOT_STARTED: -	case I40E_DCBX_STATUS_MULTIPLE_PEERS: -	default: -		break; +	} else if (hw->dcbx_status == I40E_DCBX_STATUS_DISABLED) { +		return I40E_ERR_NOT_READY;  	}  	/* Configure the LLDP MIB change event */ -	ret = i40e_aq_cfg_lldp_mib_change_event(hw, true, NULL); -	if (ret) -		return ret; +	if (enable_mib_change) +		ret = i40e_aq_cfg_lldp_mib_change_event(hw, true, NULL);  	return ret;  } diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.h b/drivers/net/ethernet/intel/i40e/i40e_dcb.h index 2b748a60a843..ddb48ae7cce4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb.h +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.h @@ -124,5 +124,5 @@ i40e_status i40e_aq_get_dcb_config(struct i40e_hw *hw, u8 mib_type,  					     u8 bridgetype,  					     struct i40e_dcbx_config *dcbcfg);  i40e_status i40e_get_dcb_config(struct i40e_hw *hw); -i40e_status i40e_init_dcb(struct i40e_hw *hw); +i40e_status i40e_init_dcb(struct i40e_hw *hw, bool enable_mib_change);  #endif /* _I40E_DCB_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_ddp.c b/drivers/net/ethernet/intel/i40e/i40e_ddp.c new file mode 100644 index 000000000000..5e08f100c413 --- /dev/null +++ b/drivers/net/ethernet/intel/i40e/i40e_ddp.c @@ -0,0 +1,481 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2018 Intel Corporation. */ + +#include "i40e.h" + +#include <linux/firmware.h> + +/** + * i40e_ddp_profiles_eq - checks if DDP profiles are the equivalent + * @a: new profile info + * @b: old profile info + * + * checks if DDP profiles are the equivalent. + * Returns true if profiles are the same. + **/ +static bool i40e_ddp_profiles_eq(struct i40e_profile_info *a, +				 struct i40e_profile_info *b) +{ +	return a->track_id == b->track_id && +		!memcmp(&a->version, &b->version, sizeof(a->version)) && +		!memcmp(&a->name, &b->name, I40E_DDP_NAME_SIZE); +} + +/** + * i40e_ddp_does_profile_exist - checks if DDP profile loaded already + * @hw: HW data structure + * @pinfo: DDP profile information structure + * + * checks if DDP profile loaded already. + * Returns >0 if the profile exists. + * Returns  0 if the profile is absent. + * Returns <0 if error. + **/ +static int i40e_ddp_does_profile_exist(struct i40e_hw *hw, +				       struct i40e_profile_info *pinfo) +{ +	struct i40e_ddp_profile_list *profile_list; +	u8 buff[I40E_PROFILE_LIST_SIZE]; +	i40e_status status; +	int i; + +	status = i40e_aq_get_ddp_list(hw, buff, I40E_PROFILE_LIST_SIZE, 0, +				      NULL); +	if (status) +		return -1; + +	profile_list = (struct i40e_ddp_profile_list *)buff; +	for (i = 0; i < profile_list->p_count; i++) { +		if (i40e_ddp_profiles_eq(pinfo, &profile_list->p_info[i])) +			return 1; +	} +	return 0; +} + +/** + * i40e_ddp_profiles_overlap - checks if DDP profiles overlap. + * @new: new profile info + * @old: old profile info + * + * checks if DDP profiles overlap. + * Returns true if profiles are overlap. + **/ +static bool i40e_ddp_profiles_overlap(struct i40e_profile_info *new, +				      struct i40e_profile_info *old) +{ +	unsigned int group_id_old = (u8)((old->track_id & 0x00FF0000) >> 16); +	unsigned int group_id_new = (u8)((new->track_id & 0x00FF0000) >> 16); + +	/* 0x00 group must be only the first */ +	if (group_id_new == 0) +		return true; +	/* 0xFF group is compatible with anything else */ +	if (group_id_new == 0xFF || group_id_old == 0xFF) +		return false; +	/* otherwise only profiles from the same group are compatible*/ +	return group_id_old != group_id_new; +} + +/** + * i40e_ddp_does_profiles_ - checks if DDP overlaps with existing one. + * @hw: HW data structure + * @pinfo: DDP profile information structure + * + * checks if DDP profile overlaps with existing one. + * Returns >0 if the profile overlaps. + * Returns  0 if the profile is ok. + * Returns <0 if error. + **/ +static int i40e_ddp_does_profile_overlap(struct i40e_hw *hw, +					 struct i40e_profile_info *pinfo) +{ +	struct i40e_ddp_profile_list *profile_list; +	u8 buff[I40E_PROFILE_LIST_SIZE]; +	i40e_status status; +	int i; + +	status = i40e_aq_get_ddp_list(hw, buff, I40E_PROFILE_LIST_SIZE, 0, +				      NULL); +	if (status) +		return -EIO; + +	profile_list = (struct i40e_ddp_profile_list *)buff; +	for (i = 0; i < profile_list->p_count; i++) { +		if (i40e_ddp_profiles_overlap(pinfo, +					      &profile_list->p_info[i])) +			return 1; +	} +	return 0; +} + +/** + * i40e_add_pinfo + * @hw: pointer to the hardware structure + * @profile: pointer to the profile segment of the package + * @profile_info_sec: buffer for information section + * @track_id: package tracking id + * + * Register a profile to the list of loaded profiles. + */ +static enum i40e_status_code +i40e_add_pinfo(struct i40e_hw *hw, struct i40e_profile_segment *profile, +	       u8 *profile_info_sec, u32 track_id) +{ +	struct i40e_profile_section_header *sec; +	struct i40e_profile_info *pinfo; +	i40e_status status; +	u32 offset = 0, info = 0; + +	sec = (struct i40e_profile_section_header *)profile_info_sec; +	sec->tbl_size = 1; +	sec->data_end = sizeof(struct i40e_profile_section_header) + +			sizeof(struct i40e_profile_info); +	sec->section.type = SECTION_TYPE_INFO; +	sec->section.offset = sizeof(struct i40e_profile_section_header); +	sec->section.size = sizeof(struct i40e_profile_info); +	pinfo = (struct i40e_profile_info *)(profile_info_sec + +					     sec->section.offset); +	pinfo->track_id = track_id; +	pinfo->version = profile->version; +	pinfo->op = I40E_DDP_ADD_TRACKID; + +	/* Clear reserved field */ +	memset(pinfo->reserved, 0, sizeof(pinfo->reserved)); +	memcpy(pinfo->name, profile->name, I40E_DDP_NAME_SIZE); + +	status = i40e_aq_write_ddp(hw, (void *)sec, sec->data_end, +				   track_id, &offset, &info, NULL); +	return status; +} + +/** + * i40e_del_pinfo - delete DDP profile info from NIC + * @hw: HW data structure + * @profile: DDP profile segment to be deleted + * @profile_info_sec: DDP profile section header + * @track_id: track ID of the profile for deletion + * + * Removes DDP profile from the NIC. + **/ +static enum i40e_status_code +i40e_del_pinfo(struct i40e_hw *hw, struct i40e_profile_segment *profile, +	       u8 *profile_info_sec, u32 track_id) +{ +	struct i40e_profile_section_header *sec; +	struct i40e_profile_info *pinfo; +	i40e_status status; +	u32 offset = 0, info = 0; + +	sec = (struct i40e_profile_section_header *)profile_info_sec; +	sec->tbl_size = 1; +	sec->data_end = sizeof(struct i40e_profile_section_header) + +			sizeof(struct i40e_profile_info); +	sec->section.type = SECTION_TYPE_INFO; +	sec->section.offset = sizeof(struct i40e_profile_section_header); +	sec->section.size = sizeof(struct i40e_profile_info); +	pinfo = (struct i40e_profile_info *)(profile_info_sec + +					     sec->section.offset); +	pinfo->track_id = track_id; +	pinfo->version = profile->version; +	pinfo->op = I40E_DDP_REMOVE_TRACKID; + +	/* Clear reserved field */ +	memset(pinfo->reserved, 0, sizeof(pinfo->reserved)); +	memcpy(pinfo->name, profile->name, I40E_DDP_NAME_SIZE); + +	status = i40e_aq_write_ddp(hw, (void *)sec, sec->data_end, +				   track_id, &offset, &info, NULL); +	return status; +} + +/** + * i40e_ddp_is_pkg_hdr_valid - performs basic pkg header integrity checks + * @netdev: net device structure (for logging purposes) + * @pkg_hdr: pointer to package header + * @size_huge: size of the whole DDP profile package in size_t + * + * Checks correctness of pkg header: Version, size too big/small, and + * all segment offsets alignment and boundaries. This function lets + * reject non DDP profile file to be loaded by administrator mistake. + **/ +static bool i40e_ddp_is_pkg_hdr_valid(struct net_device *netdev, +				      struct i40e_package_header *pkg_hdr, +				      size_t size_huge) +{ +	u32 size = 0xFFFFFFFFU & size_huge; +	u32 pkg_hdr_size; +	u32 segment; + +	if (!pkg_hdr) +		return false; + +	if (pkg_hdr->version.major > 0) { +		struct i40e_ddp_version ver = pkg_hdr->version; + +		netdev_err(netdev, "Unsupported DDP profile version %u.%u.%u.%u", +			   ver.major, ver.minor, ver.update, ver.draft); +		return false; +	} +	if (size_huge > size) { +		netdev_err(netdev, "Invalid DDP profile - size is bigger than 4G"); +		return false; +	} +	if (size < (sizeof(struct i40e_package_header) + +		sizeof(struct i40e_metadata_segment) + sizeof(u32) * 2)) { +		netdev_err(netdev, "Invalid DDP profile - size is too small."); +		return false; +	} + +	pkg_hdr_size = sizeof(u32) * (pkg_hdr->segment_count + 2U); +	if (size < pkg_hdr_size) { +		netdev_err(netdev, "Invalid DDP profile - too many segments"); +		return false; +	} +	for (segment = 0; segment < pkg_hdr->segment_count; ++segment) { +		u32 offset = pkg_hdr->segment_offset[segment]; + +		if (0xFU & offset) { +			netdev_err(netdev, +				   "Invalid DDP profile %u segment alignment", +				   segment); +			return false; +		} +		if (pkg_hdr_size > offset || offset >= size) { +			netdev_err(netdev, +				   "Invalid DDP profile %u segment offset", +				   segment); +			return false; +		} +	} + +	return true; +} + +/** + * i40e_ddp_load - performs DDP loading + * @netdev: net device structure + * @data: buffer containing recipe file + * @size: size of the buffer + * @is_add: true when loading profile, false when rolling back the previous one + * + * Checks correctness and loads DDP profile to the NIC. The function is + * also used for rolling back previously loaded profile. + **/ +int i40e_ddp_load(struct net_device *netdev, const u8 *data, size_t size, +		  bool is_add) +{ +	u8 profile_info_sec[sizeof(struct i40e_profile_section_header) + +			    sizeof(struct i40e_profile_info)]; +	struct i40e_metadata_segment *metadata_hdr; +	struct i40e_profile_segment *profile_hdr; +	struct i40e_profile_info pinfo; +	struct i40e_package_header *pkg_hdr; +	i40e_status status; +	struct i40e_netdev_priv *np = netdev_priv(netdev); +	struct i40e_vsi *vsi = np->vsi; +	struct i40e_pf *pf = vsi->back; +	u32 track_id; +	int istatus; + +	pkg_hdr = (struct i40e_package_header *)data; +	if (!i40e_ddp_is_pkg_hdr_valid(netdev, pkg_hdr, size)) +		return -EINVAL; + +	if (size < (sizeof(struct i40e_package_header) + +		    sizeof(struct i40e_metadata_segment) + sizeof(u32) * 2)) { +		netdev_err(netdev, "Invalid DDP recipe size."); +		return -EINVAL; +	} + +	/* Find beginning of segment data in buffer */ +	metadata_hdr = (struct i40e_metadata_segment *) +		i40e_find_segment_in_package(SEGMENT_TYPE_METADATA, pkg_hdr); +	if (!metadata_hdr) { +		netdev_err(netdev, "Failed to find metadata segment in DDP recipe."); +		return -EINVAL; +	} + +	track_id = metadata_hdr->track_id; +	profile_hdr = (struct i40e_profile_segment *) +		i40e_find_segment_in_package(SEGMENT_TYPE_I40E, pkg_hdr); +	if (!profile_hdr) { +		netdev_err(netdev, "Failed to find profile segment in DDP recipe."); +		return -EINVAL; +	} + +	pinfo.track_id = track_id; +	pinfo.version = profile_hdr->version; +	if (is_add) +		pinfo.op = I40E_DDP_ADD_TRACKID; +	else +		pinfo.op = I40E_DDP_REMOVE_TRACKID; + +	memcpy(pinfo.name, profile_hdr->name, I40E_DDP_NAME_SIZE); + +	/* Check if profile data already exists*/ +	istatus = i40e_ddp_does_profile_exist(&pf->hw, &pinfo); +	if (istatus < 0) { +		netdev_err(netdev, "Failed to fetch loaded profiles."); +		return istatus; +	} +	if (is_add) { +		if (istatus > 0) { +			netdev_err(netdev, "DDP profile already loaded."); +			return -EINVAL; +		} +		istatus = i40e_ddp_does_profile_overlap(&pf->hw, &pinfo); +		if (istatus < 0) { +			netdev_err(netdev, "Failed to fetch loaded profiles."); +			return istatus; +		} +		if (istatus > 0) { +			netdev_err(netdev, "DDP profile overlaps with existing one."); +			return -EINVAL; +		} +	} else { +		if (istatus == 0) { +			netdev_err(netdev, +				   "DDP profile for deletion does not exist."); +			return -EINVAL; +		} +	} + +	/* Load profile data */ +	if (is_add) { +		status = i40e_write_profile(&pf->hw, profile_hdr, track_id); +		if (status) { +			if (status == I40E_ERR_DEVICE_NOT_SUPPORTED) { +				netdev_err(netdev, +					   "Profile is not supported by the device."); +				return -EPERM; +			} +			netdev_err(netdev, "Failed to write DDP profile."); +			return -EIO; +		} +	} else { +		status = i40e_rollback_profile(&pf->hw, profile_hdr, track_id); +		if (status) { +			netdev_err(netdev, "Failed to remove DDP profile."); +			return -EIO; +		} +	} + +	/* Add/remove profile to/from profile list in FW */ +	if (is_add) { +		status = i40e_add_pinfo(&pf->hw, profile_hdr, profile_info_sec, +					track_id); +		if (status) { +			netdev_err(netdev, "Failed to add DDP profile info."); +			return -EIO; +		} +	} else { +		status = i40e_del_pinfo(&pf->hw, profile_hdr, profile_info_sec, +					track_id); +		if (status) { +			netdev_err(netdev, "Failed to restore DDP profile info."); +			return -EIO; +		} +	} + +	return 0; +} + +/** + * i40e_ddp_restore - restore previously loaded profile and remove from list + * @pf: PF data struct + * + * Restores previously loaded profile stored on the list in driver memory. + * After rolling back removes entry from the list. + **/ +static int i40e_ddp_restore(struct i40e_pf *pf) +{ +	struct i40e_ddp_old_profile_list *entry; +	struct net_device *netdev = pf->vsi[pf->lan_vsi]->netdev; +	int status = 0; + +	if (!list_empty(&pf->ddp_old_prof)) { +		entry = list_first_entry(&pf->ddp_old_prof, +					 struct i40e_ddp_old_profile_list, +					 list); +		status = i40e_ddp_load(netdev, entry->old_ddp_buf, +				       entry->old_ddp_size, false); +		list_del(&entry->list); +		kfree(entry); +	} +	return status; +} + +/** + * i40e_ddp_flash - callback function for ethtool flash feature + * @netdev: net device structure + * @flash: kernel flash structure + * + * Ethtool callback function used for loading and unloading DDP profiles. + **/ +int i40e_ddp_flash(struct net_device *netdev, struct ethtool_flash *flash) +{ +	const struct firmware *ddp_config; +	struct i40e_netdev_priv *np = netdev_priv(netdev); +	struct i40e_vsi *vsi = np->vsi; +	struct i40e_pf *pf = vsi->back; +	int status = 0; + +	/* Check for valid region first */ +	if (flash->region != I40_DDP_FLASH_REGION) { +		netdev_err(netdev, "Requested firmware region is not recognized by this driver."); +		return -EINVAL; +	} +	if (pf->hw.bus.func != 0) { +		netdev_err(netdev, "Any DDP operation is allowed only on Phy0 NIC interface"); +		return -EINVAL; +	} + +	/* If the user supplied "-" instead of file name rollback previously +	 * stored profile. +	 */ +	if (strncmp(flash->data, "-", 2) != 0) { +		struct i40e_ddp_old_profile_list *list_entry; +		char profile_name[sizeof(I40E_DDP_PROFILE_PATH) +				  + I40E_DDP_PROFILE_NAME_MAX]; + +		profile_name[sizeof(profile_name) - 1] = 0; +		strncpy(profile_name, I40E_DDP_PROFILE_PATH, +			sizeof(profile_name) - 1); +		strncat(profile_name, flash->data, I40E_DDP_PROFILE_NAME_MAX); +		/* Load DDP recipe. */ +		status = request_firmware(&ddp_config, profile_name, +					  &netdev->dev); +		if (status) { +			netdev_err(netdev, "DDP recipe file request failed."); +			return status; +		} + +		status = i40e_ddp_load(netdev, ddp_config->data, +				       ddp_config->size, true); + +		if (!status) { +			list_entry = +			  kzalloc(sizeof(struct i40e_ddp_old_profile_list) + +				  ddp_config->size, GFP_KERNEL); +			if (!list_entry) { +				netdev_info(netdev, "Failed to allocate memory for previous DDP profile data."); +				netdev_info(netdev, "New profile loaded but roll-back will be impossible."); +			} else { +				memcpy(list_entry->old_ddp_buf, +				       ddp_config->data, ddp_config->size); +				list_entry->old_ddp_size = ddp_config->size; +				list_add(&list_entry->list, &pf->ddp_old_prof); +			} +		} + +		release_firmware(ddp_config); +	} else { +		if (!list_empty(&pf->ddp_old_prof)) { +			status = i40e_ddp_restore(pf); +		} else { +			netdev_warn(netdev, "There is no DDP profile to restore."); +			status = -ENOENT; +		} +	} +	return status; +} diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 7874d0ec7fb0..9eaea1bee4a1 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -535,9 +535,12 @@ static void i40e_phy_type_to_ethtool(struct i40e_pf *pf,  			ethtool_link_ksettings_add_link_mode(ks, advertising,  							     1000baseT_Full);  	} -	if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_SR4) +	if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_SR4) {  		ethtool_link_ksettings_add_link_mode(ks, supported,  						     40000baseSR4_Full); +		ethtool_link_ksettings_add_link_mode(ks, advertising, +						     40000baseSR4_Full); +	}  	if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_LR4)  		ethtool_link_ksettings_add_link_mode(ks, supported,  						     40000baseLR4_Full); @@ -724,6 +727,8 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw,  	case I40E_PHY_TYPE_40GBASE_SR4:  		ethtool_link_ksettings_add_link_mode(ks, supported,  						     40000baseSR4_Full); +		ethtool_link_ksettings_add_link_mode(ks, advertising, +						     40000baseSR4_Full);  		break;  	case I40E_PHY_TYPE_40GBASE_LR4:  		ethtool_link_ksettings_add_link_mode(ks, supported, @@ -5171,6 +5176,7 @@ static const struct ethtool_ops i40e_ethtool_ops = {  	.set_link_ksettings	= i40e_set_link_ksettings,  	.get_fecparam = i40e_get_fec_param,  	.set_fecparam = i40e_set_fec_param, +	.flash_device = i40e_ddp_flash,  };  void i40e_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index b1c265012c8a..65c2b9d2652b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2107,11 +2107,22 @@ void i40e_aqc_add_filters(struct i40e_vsi *vsi, const char *vsi_name,  	fcnt = i40e_update_filter_state(num_add, list, add_head);  	if (fcnt != num_add) { -		set_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state); -		dev_warn(&vsi->back->pdev->dev, -			 "Error %s adding RX filters on %s, promiscuous mode forced on\n", -			 i40e_aq_str(hw, aq_err), -			 vsi_name); +		if (vsi->type == I40E_VSI_MAIN) { +			set_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state); +			dev_warn(&vsi->back->pdev->dev, +				 "Error %s adding RX filters on %s, promiscuous mode forced on\n", +				 i40e_aq_str(hw, aq_err), vsi_name); +		} else if (vsi->type == I40E_VSI_SRIOV || +			   vsi->type == I40E_VSI_VMDQ1 || +			   vsi->type == I40E_VSI_VMDQ2) { +			dev_warn(&vsi->back->pdev->dev, +				 "Error %s adding RX filters on %s, please set promiscuous on manually for %s\n", +				 i40e_aq_str(hw, aq_err), vsi_name, vsi_name); +		} else { +			dev_warn(&vsi->back->pdev->dev, +				 "Error %s adding RX filters on %s, incorrect VSI type: %i.\n", +				 i40e_aq_str(hw, aq_err), vsi_name, vsi->type); +		}  	}  } @@ -2654,6 +2665,10 @@ void i40e_vlan_stripping_enable(struct i40e_vsi *vsi)  	struct i40e_vsi_context ctxt;  	i40e_status ret; +	/* Don't modify stripping options if a port VLAN is active */ +	if (vsi->info.pvid) +		return; +  	if ((vsi->info.valid_sections &  	     cpu_to_le16(I40E_AQ_VSI_PROP_VLAN_VALID)) &&  	    ((vsi->info.port_vlan_flags & I40E_AQ_VSI_PVLAN_MODE_MASK) == 0)) @@ -2684,6 +2699,10 @@ void i40e_vlan_stripping_disable(struct i40e_vsi *vsi)  	struct i40e_vsi_context ctxt;  	i40e_status ret; +	/* Don't modify stripping options if a port VLAN is active */ +	if (vsi->info.pvid) +		return; +  	if ((vsi->info.valid_sections &  	     cpu_to_le16(I40E_AQ_VSI_PROP_VLAN_VALID)) &&  	    ((vsi->info.port_vlan_flags & I40E_AQ_VSI_PVLAN_EMOD_MASK) == @@ -6403,7 +6422,7 @@ static int i40e_init_pf_dcb(struct i40e_pf *pf)  		goto out;  	/* Get the initial DCB configuration */ -	err = i40e_init_dcb(hw); +	err = i40e_init_dcb(hw, true);  	if (!err) {  		/* Device/Function is not DCBX capable */  		if ((!hw->func_caps.dcb) || @@ -6846,10 +6865,12 @@ static int i40e_setup_tc(struct net_device *netdev, void *type_data)  	struct i40e_pf *pf = vsi->back;  	u8 enabled_tc = 0, num_tc, hw;  	bool need_reset = false; +	int old_queue_pairs;  	int ret = -EINVAL;  	u16 mode;  	int i; +	old_queue_pairs = vsi->num_queue_pairs;  	num_tc = mqprio_qopt->qopt.num_tc;  	hw = mqprio_qopt->qopt.hw;  	mode = mqprio_qopt->mode; @@ -6950,6 +6971,7 @@ config_tc:  		}  		ret = i40e_configure_queue_channels(vsi);  		if (ret) { +			vsi->num_queue_pairs = old_queue_pairs;  			netdev_info(netdev,  				    "Failed configuring queue channels\n");  			need_reset = true; @@ -9290,6 +9312,11 @@ static void i40e_prep_for_reset(struct i40e_pf *pf, bool lock_acquired)  			dev_warn(&pf->pdev->dev,  				 "shutdown_lan_hmc failed: %d\n", ret);  	} + +	/* Save the current PTP time so that we can restore the time after the +	 * reset completes. +	 */ +	i40e_ptp_save_hw_time(pf);  }  /** @@ -13984,6 +14011,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  	INIT_LIST_HEAD(&pf->l3_flex_pit_list);  	INIT_LIST_HEAD(&pf->l4_flex_pit_list); +	INIT_LIST_HEAD(&pf->ddp_old_prof);  	/* set up the locks for the AQ, do this only once in probe  	 * and destroy them only once in remove @@ -14042,7 +14070,11 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  	if (err) {  		if (err == I40E_ERR_FIRMWARE_API_VERSION)  			dev_info(&pdev->dev, -				 "The driver for the device stopped because the NVM image is newer than expected. You must install the most recent version of the network driver.\n"); +				 "The driver for the device stopped because the NVM image v%u.%u is newer than expected v%u.%u. You must install the most recent version of the network driver.\n", +				 hw->aq.api_maj_ver, +				 hw->aq.api_min_ver, +				 I40E_FW_API_VERSION_MAJOR, +				 I40E_FW_MINOR_VERSION(hw));  		else  			dev_info(&pdev->dev,  				 "The driver for the device stopped because the device firmware failed to init. Try updating your NVM image.\n"); @@ -14060,10 +14092,18 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  	if (hw->aq.api_maj_ver == I40E_FW_API_VERSION_MAJOR &&  	    hw->aq.api_min_ver > I40E_FW_MINOR_VERSION(hw))  		dev_info(&pdev->dev, -			 "The driver for the device detected a newer version of the NVM image than expected. Please install the most recent version of the network driver.\n"); +			 "The driver for the device detected a newer version of the NVM image v%u.%u than expected v%u.%u. Please install the most recent version of the network driver.\n", +			 hw->aq.api_maj_ver, +			 hw->aq.api_min_ver, +			 I40E_FW_API_VERSION_MAJOR, +			 I40E_FW_MINOR_VERSION(hw));  	else if (hw->aq.api_maj_ver == 1 && hw->aq.api_min_ver < 4)  		dev_info(&pdev->dev, -			 "The driver for the device detected an older version of the NVM image than expected. Please update the NVM image.\n"); +			 "The driver for the device detected an older version of the NVM image v%u.%u than expected v%u.%u. Please update the NVM image.\n", +			 hw->aq.api_maj_ver, +			 hw->aq.api_min_ver, +			 I40E_FW_API_VERSION_MAJOR, +			 I40E_FW_MINOR_VERSION(hw));  	i40e_verify_eeprom(pf); diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h index e08d754824b1..663c8bf4d3d8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h @@ -429,10 +429,16 @@ i40e_status i40e_aq_get_ddp_list(struct i40e_hw *hw, void *buff,  struct i40e_generic_seg_header *  i40e_find_segment_in_package(u32 segment_type,  			     struct i40e_package_header *pkg_header); +struct i40e_profile_section_header * +i40e_find_section_in_profile(u32 section_type, +			     struct i40e_profile_segment *profile);  enum i40e_status_code  i40e_write_profile(struct i40e_hw *hw, struct i40e_profile_segment *i40e_seg,  		   u32 track_id);  enum i40e_status_code +i40e_rollback_profile(struct i40e_hw *hw, struct i40e_profile_segment *i40e_seg, +		      u32 track_id); +enum i40e_status_code  i40e_add_pinfo_to_list(struct i40e_hw *hw,  		       struct i40e_profile_segment *profile,  		       u8 *profile_info_sec, u32 track_id); diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 31575c0bb884..439c35f0c581 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -725,16 +725,68 @@ static long i40e_ptp_create_clock(struct i40e_pf *pf)  	pf->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;  	pf->tstamp_config.tx_type = HWTSTAMP_TX_OFF; +	/* Set the previous "reset" time to the current Kernel clock time */ +	pf->ptp_prev_hw_time = ktime_to_timespec64(ktime_get_real()); +	pf->ptp_reset_start = ktime_get(); +  	return 0;  }  /** + * i40e_ptp_save_hw_time - Save the current PTP time as ptp_prev_hw_time + * @pf: Board private structure + * + * Read the current PTP time and save it into pf->ptp_prev_hw_time. This should + * be called at the end of preparing to reset, just before hardware reset + * occurs, in order to preserve the PTP time as close as possible across + * resets. + */ +void i40e_ptp_save_hw_time(struct i40e_pf *pf) +{ +	/* don't try to access the PTP clock if it's not enabled */ +	if (!(pf->flags & I40E_FLAG_PTP)) +		return; + +	i40e_ptp_gettimex(&pf->ptp_caps, &pf->ptp_prev_hw_time, NULL); +	/* Get a monotonic starting time for this reset */ +	pf->ptp_reset_start = ktime_get(); +} + +/** + * i40e_ptp_restore_hw_time - Restore the ptp_prev_hw_time + delta to PTP regs + * @pf: Board private structure + * + * Restore the PTP hardware clock registers. We previously cached the PTP + * hardware time as pf->ptp_prev_hw_time. To be as accurate as possible, + * update this value based on the time delta since the time was saved, using + * CLOCK_MONOTONIC (via ktime_get()) to calculate the time difference. + * + * This ensures that the hardware clock is restored to nearly what it should + * have been if a reset had not occurred. + */ +void i40e_ptp_restore_hw_time(struct i40e_pf *pf) +{ +	ktime_t delta = ktime_sub(ktime_get(), pf->ptp_reset_start); + +	/* Update the previous HW time with the ktime delta */ +	timespec64_add_ns(&pf->ptp_prev_hw_time, ktime_to_ns(delta)); + +	/* Restore the hardware clock registers */ +	i40e_ptp_settime(&pf->ptp_caps, &pf->ptp_prev_hw_time); +} + +/**   * i40e_ptp_init - Initialize the 1588 support after device probe or reset   * @pf: Board private structure   *   * This function sets device up for 1588 support. The first time it is run, it   * will create a PHC clock device. It does not create a clock device if one   * already exists. It also reconfigures the device after a reset. + * + * The first time a clock is created, i40e_ptp_create_clock will set + * pf->ptp_prev_hw_time to the current system time. During resets, it is + * expected that this timespec will be set to the last known PTP clock time, + * in order to preserve the clock time as close as possible across a reset.   **/  void i40e_ptp_init(struct i40e_pf *pf)  { @@ -766,7 +818,6 @@ void i40e_ptp_init(struct i40e_pf *pf)  		dev_err(&pf->pdev->dev, "%s: ptp_clock_register failed\n",  			__func__);  	} else if (pf->ptp_clock) { -		struct timespec64 ts;  		u32 regval;  		if (pf->hw.debug_mask & I40E_DEBUG_LAN) @@ -787,9 +838,8 @@ void i40e_ptp_init(struct i40e_pf *pf)  		/* reset timestamping mode */  		i40e_ptp_set_timestamp_mode(pf, &pf->tstamp_config); -		/* Set the clock value. */ -		ts = ktime_to_timespec64(ktime_get_real()); -		i40e_ptp_settime(&pf->ptp_caps, &ts); +		/* Restore the clock time based on last known value */ +		i40e_ptp_restore_hw_time(pf);  	}  } diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index 2781ab91ca82..79420bcc7414 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -1527,6 +1527,8 @@ struct i40e_generic_seg_header {  struct i40e_metadata_segment {  	struct i40e_generic_seg_header header;  	struct i40e_ddp_version version; +#define I40E_DDP_TRACKID_RDONLY		0 +#define I40E_DDP_TRACKID_INVALID	0xFFFFFFFF  	u32 track_id;  	char name[I40E_DDP_NAME_SIZE];  }; @@ -1555,15 +1557,36 @@ struct i40e_profile_section_header {  	struct {  #define SECTION_TYPE_INFO	0x00000010  #define SECTION_TYPE_MMIO	0x00000800 +#define SECTION_TYPE_RB_MMIO	0x00001800  #define SECTION_TYPE_AQ		0x00000801 +#define SECTION_TYPE_RB_AQ	0x00001801  #define SECTION_TYPE_NOTE	0x80000000  #define SECTION_TYPE_NAME	0x80000001 +#define SECTION_TYPE_PROTO	0x80000002 +#define SECTION_TYPE_PCTYPE	0x80000003 +#define SECTION_TYPE_PTYPE	0x80000004  		u32 type;  		u32 offset;  		u32 size;  	} section;  }; +struct i40e_profile_tlv_section_record { +	u8 rtype; +	u8 type; +	u16 len; +	u8 data[12]; +}; + +/* Generic AQ section in proflie */ +struct i40e_profile_aq_section { +	u16 opcode; +	u16 flags; +	u8  param[16]; +	u16 datalen; +	u8  data[1]; +}; +  struct i40e_profile_info {  	u32 track_id;  	struct i40e_ddp_version version; diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 831d52bc3c9a..71cd159e7902 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -2454,8 +2454,10 @@ error_param:  				      (u8 *)&stats, sizeof(stats));  } -/* If the VF is not trusted restrict the number of MAC/VLAN it can program */ -#define I40E_VC_MAX_MAC_ADDR_PER_VF 12 +/* If the VF is not trusted restrict the number of MAC/VLAN it can program + * MAC filters: 16 for multicast, 1 for MAC, 1 for broadcast + */ +#define I40E_VC_MAX_MAC_ADDR_PER_VF (16 + 1 + 1)  #define I40E_VC_MAX_VLAN_PER_VF 8  /** diff --git a/drivers/net/ethernet/intel/iavf/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/iavf/i40e_adminq_cmd.h index af4f94a6541e..e5ae4a1c0cff 100644 --- a/drivers/net/ethernet/intel/iavf/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/iavf/i40e_adminq_cmd.h @@ -14,7 +14,7 @@  #define I40E_FW_API_VERSION_MAJOR	0x0001  #define I40E_FW_API_VERSION_MINOR_X722	0x0005 -#define I40E_FW_API_VERSION_MINOR_X710	0x0007 +#define I40E_FW_API_VERSION_MINOR_X710	0x0008  #define I40E_FW_MINOR_VERSION(_h) ((_h)->mac.type == I40E_MAC_XL710 ? \  					I40E_FW_API_VERSION_MINOR_X710 : \ | 
