162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2022, Intel Corporation. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include "ice_common.h"
562306a36Sopenharmony_ci#include "ice.h"
662306a36Sopenharmony_ci#include "ice_ddp.h"
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/* For supporting double VLAN mode, it is necessary to enable or disable certain
962306a36Sopenharmony_ci * boost tcam entries. The metadata labels names that match the following
1062306a36Sopenharmony_ci * prefixes will be saved to allow enabling double VLAN mode.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci#define ICE_DVM_PRE "BOOST_MAC_VLAN_DVM" /* enable these entries */
1362306a36Sopenharmony_ci#define ICE_SVM_PRE "BOOST_MAC_VLAN_SVM" /* disable these entries */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/* To support tunneling entries by PF, the package will append the PF number to
1662306a36Sopenharmony_ci * the label; for example TNL_VXLAN_PF0, TNL_VXLAN_PF1, TNL_VXLAN_PF2, etc.
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci#define ICE_TNL_PRE "TNL_"
1962306a36Sopenharmony_cistatic const struct ice_tunnel_type_scan tnls[] = {
2062306a36Sopenharmony_ci	{ TNL_VXLAN, "TNL_VXLAN_PF" },
2162306a36Sopenharmony_ci	{ TNL_GENEVE, "TNL_GENEVE_PF" },
2262306a36Sopenharmony_ci	{ TNL_LAST, "" }
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/**
2662306a36Sopenharmony_ci * ice_verify_pkg - verify package
2762306a36Sopenharmony_ci * @pkg: pointer to the package buffer
2862306a36Sopenharmony_ci * @len: size of the package buffer
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci * Verifies various attributes of the package file, including length, format
3162306a36Sopenharmony_ci * version, and the requirement of at least one segment.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_cistatic enum ice_ddp_state ice_verify_pkg(struct ice_pkg_hdr *pkg, u32 len)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	u32 seg_count;
3662306a36Sopenharmony_ci	u32 i;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (len < struct_size(pkg, seg_offset, 1))
3962306a36Sopenharmony_ci		return ICE_DDP_PKG_INVALID_FILE;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (pkg->pkg_format_ver.major != ICE_PKG_FMT_VER_MAJ ||
4262306a36Sopenharmony_ci	    pkg->pkg_format_ver.minor != ICE_PKG_FMT_VER_MNR ||
4362306a36Sopenharmony_ci	    pkg->pkg_format_ver.update != ICE_PKG_FMT_VER_UPD ||
4462306a36Sopenharmony_ci	    pkg->pkg_format_ver.draft != ICE_PKG_FMT_VER_DFT)
4562306a36Sopenharmony_ci		return ICE_DDP_PKG_INVALID_FILE;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	/* pkg must have at least one segment */
4862306a36Sopenharmony_ci	seg_count = le32_to_cpu(pkg->seg_count);
4962306a36Sopenharmony_ci	if (seg_count < 1)
5062306a36Sopenharmony_ci		return ICE_DDP_PKG_INVALID_FILE;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/* make sure segment array fits in package length */
5362306a36Sopenharmony_ci	if (len < struct_size(pkg, seg_offset, seg_count))
5462306a36Sopenharmony_ci		return ICE_DDP_PKG_INVALID_FILE;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* all segments must fit within length */
5762306a36Sopenharmony_ci	for (i = 0; i < seg_count; i++) {
5862306a36Sopenharmony_ci		u32 off = le32_to_cpu(pkg->seg_offset[i]);
5962306a36Sopenharmony_ci		struct ice_generic_seg_hdr *seg;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		/* segment header must fit */
6262306a36Sopenharmony_ci		if (len < off + sizeof(*seg))
6362306a36Sopenharmony_ci			return ICE_DDP_PKG_INVALID_FILE;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci		seg = (struct ice_generic_seg_hdr *)((u8 *)pkg + off);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci		/* segment body must fit */
6862306a36Sopenharmony_ci		if (len < off + le32_to_cpu(seg->seg_size))
6962306a36Sopenharmony_ci			return ICE_DDP_PKG_INVALID_FILE;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return ICE_DDP_PKG_SUCCESS;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/**
7662306a36Sopenharmony_ci * ice_free_seg - free package segment pointer
7762306a36Sopenharmony_ci * @hw: pointer to the hardware structure
7862306a36Sopenharmony_ci *
7962306a36Sopenharmony_ci * Frees the package segment pointer in the proper manner, depending on if the
8062306a36Sopenharmony_ci * segment was allocated or just the passed in pointer was stored.
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_civoid ice_free_seg(struct ice_hw *hw)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	if (hw->pkg_copy) {
8562306a36Sopenharmony_ci		devm_kfree(ice_hw_to_dev(hw), hw->pkg_copy);
8662306a36Sopenharmony_ci		hw->pkg_copy = NULL;
8762306a36Sopenharmony_ci		hw->pkg_size = 0;
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci	hw->seg = NULL;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/**
9362306a36Sopenharmony_ci * ice_chk_pkg_version - check package version for compatibility with driver
9462306a36Sopenharmony_ci * @pkg_ver: pointer to a version structure to check
9562306a36Sopenharmony_ci *
9662306a36Sopenharmony_ci * Check to make sure that the package about to be downloaded is compatible with
9762306a36Sopenharmony_ci * the driver. To be compatible, the major and minor components of the package
9862306a36Sopenharmony_ci * version must match our ICE_PKG_SUPP_VER_MAJ and ICE_PKG_SUPP_VER_MNR
9962306a36Sopenharmony_ci * definitions.
10062306a36Sopenharmony_ci */
10162306a36Sopenharmony_cistatic enum ice_ddp_state ice_chk_pkg_version(struct ice_pkg_ver *pkg_ver)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	if (pkg_ver->major > ICE_PKG_SUPP_VER_MAJ ||
10462306a36Sopenharmony_ci	    (pkg_ver->major == ICE_PKG_SUPP_VER_MAJ &&
10562306a36Sopenharmony_ci	     pkg_ver->minor > ICE_PKG_SUPP_VER_MNR))
10662306a36Sopenharmony_ci		return ICE_DDP_PKG_FILE_VERSION_TOO_HIGH;
10762306a36Sopenharmony_ci	else if (pkg_ver->major < ICE_PKG_SUPP_VER_MAJ ||
10862306a36Sopenharmony_ci		 (pkg_ver->major == ICE_PKG_SUPP_VER_MAJ &&
10962306a36Sopenharmony_ci		  pkg_ver->minor < ICE_PKG_SUPP_VER_MNR))
11062306a36Sopenharmony_ci		return ICE_DDP_PKG_FILE_VERSION_TOO_LOW;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	return ICE_DDP_PKG_SUCCESS;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/**
11662306a36Sopenharmony_ci * ice_pkg_val_buf
11762306a36Sopenharmony_ci * @buf: pointer to the ice buffer
11862306a36Sopenharmony_ci *
11962306a36Sopenharmony_ci * This helper function validates a buffer's header.
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_cistatic struct ice_buf_hdr *ice_pkg_val_buf(struct ice_buf *buf)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct ice_buf_hdr *hdr;
12462306a36Sopenharmony_ci	u16 section_count;
12562306a36Sopenharmony_ci	u16 data_end;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	hdr = (struct ice_buf_hdr *)buf->buf;
12862306a36Sopenharmony_ci	/* verify data */
12962306a36Sopenharmony_ci	section_count = le16_to_cpu(hdr->section_count);
13062306a36Sopenharmony_ci	if (section_count < ICE_MIN_S_COUNT || section_count > ICE_MAX_S_COUNT)
13162306a36Sopenharmony_ci		return NULL;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	data_end = le16_to_cpu(hdr->data_end);
13462306a36Sopenharmony_ci	if (data_end < ICE_MIN_S_DATA_END || data_end > ICE_MAX_S_DATA_END)
13562306a36Sopenharmony_ci		return NULL;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	return hdr;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/**
14162306a36Sopenharmony_ci * ice_find_buf_table
14262306a36Sopenharmony_ci * @ice_seg: pointer to the ice segment
14362306a36Sopenharmony_ci *
14462306a36Sopenharmony_ci * Returns the address of the buffer table within the ice segment.
14562306a36Sopenharmony_ci */
14662306a36Sopenharmony_cistatic struct ice_buf_table *ice_find_buf_table(struct ice_seg *ice_seg)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	struct ice_nvm_table *nvms = (struct ice_nvm_table *)
14962306a36Sopenharmony_ci		(ice_seg->device_table + le32_to_cpu(ice_seg->device_table_count));
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return (__force struct ice_buf_table *)(nvms->vers +
15262306a36Sopenharmony_ci						le32_to_cpu(nvms->table_count));
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/**
15662306a36Sopenharmony_ci * ice_pkg_enum_buf
15762306a36Sopenharmony_ci * @ice_seg: pointer to the ice segment (or NULL on subsequent calls)
15862306a36Sopenharmony_ci * @state: pointer to the enum state
15962306a36Sopenharmony_ci *
16062306a36Sopenharmony_ci * This function will enumerate all the buffers in the ice segment. The first
16162306a36Sopenharmony_ci * call is made with the ice_seg parameter non-NULL; on subsequent calls,
16262306a36Sopenharmony_ci * ice_seg is set to NULL which continues the enumeration. When the function
16362306a36Sopenharmony_ci * returns a NULL pointer, then the end of the buffers has been reached, or an
16462306a36Sopenharmony_ci * unexpected value has been detected (for example an invalid section count or
16562306a36Sopenharmony_ci * an invalid buffer end value).
16662306a36Sopenharmony_ci */
16762306a36Sopenharmony_cistatic struct ice_buf_hdr *ice_pkg_enum_buf(struct ice_seg *ice_seg,
16862306a36Sopenharmony_ci					    struct ice_pkg_enum *state)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	if (ice_seg) {
17162306a36Sopenharmony_ci		state->buf_table = ice_find_buf_table(ice_seg);
17262306a36Sopenharmony_ci		if (!state->buf_table)
17362306a36Sopenharmony_ci			return NULL;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci		state->buf_idx = 0;
17662306a36Sopenharmony_ci		return ice_pkg_val_buf(state->buf_table->buf_array);
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (++state->buf_idx < le32_to_cpu(state->buf_table->buf_count))
18062306a36Sopenharmony_ci		return ice_pkg_val_buf(state->buf_table->buf_array +
18162306a36Sopenharmony_ci				       state->buf_idx);
18262306a36Sopenharmony_ci	else
18362306a36Sopenharmony_ci		return NULL;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/**
18762306a36Sopenharmony_ci * ice_pkg_advance_sect
18862306a36Sopenharmony_ci * @ice_seg: pointer to the ice segment (or NULL on subsequent calls)
18962306a36Sopenharmony_ci * @state: pointer to the enum state
19062306a36Sopenharmony_ci *
19162306a36Sopenharmony_ci * This helper function will advance the section within the ice segment,
19262306a36Sopenharmony_ci * also advancing the buffer if needed.
19362306a36Sopenharmony_ci */
19462306a36Sopenharmony_cistatic bool ice_pkg_advance_sect(struct ice_seg *ice_seg,
19562306a36Sopenharmony_ci				 struct ice_pkg_enum *state)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	if (!ice_seg && !state->buf)
19862306a36Sopenharmony_ci		return false;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (!ice_seg && state->buf)
20162306a36Sopenharmony_ci		if (++state->sect_idx < le16_to_cpu(state->buf->section_count))
20262306a36Sopenharmony_ci			return true;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	state->buf = ice_pkg_enum_buf(ice_seg, state);
20562306a36Sopenharmony_ci	if (!state->buf)
20662306a36Sopenharmony_ci		return false;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/* start of new buffer, reset section index */
20962306a36Sopenharmony_ci	state->sect_idx = 0;
21062306a36Sopenharmony_ci	return true;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/**
21462306a36Sopenharmony_ci * ice_pkg_enum_section
21562306a36Sopenharmony_ci * @ice_seg: pointer to the ice segment (or NULL on subsequent calls)
21662306a36Sopenharmony_ci * @state: pointer to the enum state
21762306a36Sopenharmony_ci * @sect_type: section type to enumerate
21862306a36Sopenharmony_ci *
21962306a36Sopenharmony_ci * This function will enumerate all the sections of a particular type in the
22062306a36Sopenharmony_ci * ice segment. The first call is made with the ice_seg parameter non-NULL;
22162306a36Sopenharmony_ci * on subsequent calls, ice_seg is set to NULL which continues the enumeration.
22262306a36Sopenharmony_ci * When the function returns a NULL pointer, then the end of the matching
22362306a36Sopenharmony_ci * sections has been reached.
22462306a36Sopenharmony_ci */
22562306a36Sopenharmony_civoid *ice_pkg_enum_section(struct ice_seg *ice_seg, struct ice_pkg_enum *state,
22662306a36Sopenharmony_ci			   u32 sect_type)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	u16 offset, size;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (ice_seg)
23162306a36Sopenharmony_ci		state->type = sect_type;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (!ice_pkg_advance_sect(ice_seg, state))
23462306a36Sopenharmony_ci		return NULL;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* scan for next matching section */
23762306a36Sopenharmony_ci	while (state->buf->section_entry[state->sect_idx].type !=
23862306a36Sopenharmony_ci	       cpu_to_le32(state->type))
23962306a36Sopenharmony_ci		if (!ice_pkg_advance_sect(NULL, state))
24062306a36Sopenharmony_ci			return NULL;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/* validate section */
24362306a36Sopenharmony_ci	offset = le16_to_cpu(state->buf->section_entry[state->sect_idx].offset);
24462306a36Sopenharmony_ci	if (offset < ICE_MIN_S_OFF || offset > ICE_MAX_S_OFF)
24562306a36Sopenharmony_ci		return NULL;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	size = le16_to_cpu(state->buf->section_entry[state->sect_idx].size);
24862306a36Sopenharmony_ci	if (size < ICE_MIN_S_SZ || size > ICE_MAX_S_SZ)
24962306a36Sopenharmony_ci		return NULL;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	/* make sure the section fits in the buffer */
25262306a36Sopenharmony_ci	if (offset + size > ICE_PKG_BUF_SIZE)
25362306a36Sopenharmony_ci		return NULL;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	state->sect_type =
25662306a36Sopenharmony_ci		le32_to_cpu(state->buf->section_entry[state->sect_idx].type);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/* calc pointer to this section */
25962306a36Sopenharmony_ci	state->sect =
26062306a36Sopenharmony_ci		((u8 *)state->buf) +
26162306a36Sopenharmony_ci		le16_to_cpu(state->buf->section_entry[state->sect_idx].offset);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	return state->sect;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci/**
26762306a36Sopenharmony_ci * ice_pkg_enum_entry
26862306a36Sopenharmony_ci * @ice_seg: pointer to the ice segment (or NULL on subsequent calls)
26962306a36Sopenharmony_ci * @state: pointer to the enum state
27062306a36Sopenharmony_ci * @sect_type: section type to enumerate
27162306a36Sopenharmony_ci * @offset: pointer to variable that receives the offset in the table (optional)
27262306a36Sopenharmony_ci * @handler: function that handles access to the entries into the section type
27362306a36Sopenharmony_ci *
27462306a36Sopenharmony_ci * This function will enumerate all the entries in particular section type in
27562306a36Sopenharmony_ci * the ice segment. The first call is made with the ice_seg parameter non-NULL;
27662306a36Sopenharmony_ci * on subsequent calls, ice_seg is set to NULL which continues the enumeration.
27762306a36Sopenharmony_ci * When the function returns a NULL pointer, then the end of the entries has
27862306a36Sopenharmony_ci * been reached.
27962306a36Sopenharmony_ci *
28062306a36Sopenharmony_ci * Since each section may have a different header and entry size, the handler
28162306a36Sopenharmony_ci * function is needed to determine the number and location entries in each
28262306a36Sopenharmony_ci * section.
28362306a36Sopenharmony_ci *
28462306a36Sopenharmony_ci * The offset parameter is optional, but should be used for sections that
28562306a36Sopenharmony_ci * contain an offset for each section table. For such cases, the section handler
28662306a36Sopenharmony_ci * function must return the appropriate offset + index to give the absolution
28762306a36Sopenharmony_ci * offset for each entry. For example, if the base for a section's header
28862306a36Sopenharmony_ci * indicates a base offset of 10, and the index for the entry is 2, then
28962306a36Sopenharmony_ci * section handler function should set the offset to 10 + 2 = 12.
29062306a36Sopenharmony_ci */
29162306a36Sopenharmony_cistatic void *ice_pkg_enum_entry(struct ice_seg *ice_seg,
29262306a36Sopenharmony_ci				struct ice_pkg_enum *state, u32 sect_type,
29362306a36Sopenharmony_ci				u32 *offset,
29462306a36Sopenharmony_ci				void *(*handler)(u32 sect_type, void *section,
29562306a36Sopenharmony_ci						 u32 index, u32 *offset))
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	void *entry;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	if (ice_seg) {
30062306a36Sopenharmony_ci		if (!handler)
30162306a36Sopenharmony_ci			return NULL;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		if (!ice_pkg_enum_section(ice_seg, state, sect_type))
30462306a36Sopenharmony_ci			return NULL;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci		state->entry_idx = 0;
30762306a36Sopenharmony_ci		state->handler = handler;
30862306a36Sopenharmony_ci	} else {
30962306a36Sopenharmony_ci		state->entry_idx++;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (!state->handler)
31362306a36Sopenharmony_ci		return NULL;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/* get entry */
31662306a36Sopenharmony_ci	entry = state->handler(state->sect_type, state->sect, state->entry_idx,
31762306a36Sopenharmony_ci			       offset);
31862306a36Sopenharmony_ci	if (!entry) {
31962306a36Sopenharmony_ci		/* end of a section, look for another section of this type */
32062306a36Sopenharmony_ci		if (!ice_pkg_enum_section(NULL, state, 0))
32162306a36Sopenharmony_ci			return NULL;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci		state->entry_idx = 0;
32462306a36Sopenharmony_ci		entry = state->handler(state->sect_type, state->sect,
32562306a36Sopenharmony_ci				       state->entry_idx, offset);
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	return entry;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci/**
33262306a36Sopenharmony_ci * ice_sw_fv_handler
33362306a36Sopenharmony_ci * @sect_type: section type
33462306a36Sopenharmony_ci * @section: pointer to section
33562306a36Sopenharmony_ci * @index: index of the field vector entry to be returned
33662306a36Sopenharmony_ci * @offset: ptr to variable that receives the offset in the field vector table
33762306a36Sopenharmony_ci *
33862306a36Sopenharmony_ci * This is a callback function that can be passed to ice_pkg_enum_entry.
33962306a36Sopenharmony_ci * This function treats the given section as of type ice_sw_fv_section and
34062306a36Sopenharmony_ci * enumerates offset field. "offset" is an index into the field vector table.
34162306a36Sopenharmony_ci */
34262306a36Sopenharmony_cistatic void *ice_sw_fv_handler(u32 sect_type, void *section, u32 index,
34362306a36Sopenharmony_ci			       u32 *offset)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct ice_sw_fv_section *fv_section = section;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (!section || sect_type != ICE_SID_FLD_VEC_SW)
34862306a36Sopenharmony_ci		return NULL;
34962306a36Sopenharmony_ci	if (index >= le16_to_cpu(fv_section->count))
35062306a36Sopenharmony_ci		return NULL;
35162306a36Sopenharmony_ci	if (offset)
35262306a36Sopenharmony_ci		/* "index" passed in to this function is relative to a given
35362306a36Sopenharmony_ci		 * 4k block. To get to the true index into the field vector
35462306a36Sopenharmony_ci		 * table need to add the relative index to the base_offset
35562306a36Sopenharmony_ci		 * field of this section
35662306a36Sopenharmony_ci		 */
35762306a36Sopenharmony_ci		*offset = le16_to_cpu(fv_section->base_offset) + index;
35862306a36Sopenharmony_ci	return fv_section->fv + index;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci/**
36262306a36Sopenharmony_ci * ice_get_prof_index_max - get the max profile index for used profile
36362306a36Sopenharmony_ci * @hw: pointer to the HW struct
36462306a36Sopenharmony_ci *
36562306a36Sopenharmony_ci * Calling this function will get the max profile index for used profile
36662306a36Sopenharmony_ci * and store the index number in struct ice_switch_info *switch_info
36762306a36Sopenharmony_ci * in HW for following use.
36862306a36Sopenharmony_ci */
36962306a36Sopenharmony_cistatic int ice_get_prof_index_max(struct ice_hw *hw)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	u16 prof_index = 0, j, max_prof_index = 0;
37262306a36Sopenharmony_ci	struct ice_pkg_enum state;
37362306a36Sopenharmony_ci	struct ice_seg *ice_seg;
37462306a36Sopenharmony_ci	bool flag = false;
37562306a36Sopenharmony_ci	struct ice_fv *fv;
37662306a36Sopenharmony_ci	u32 offset;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	memset(&state, 0, sizeof(state));
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	if (!hw->seg)
38162306a36Sopenharmony_ci		return -EINVAL;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	ice_seg = hw->seg;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	do {
38662306a36Sopenharmony_ci		fv = ice_pkg_enum_entry(ice_seg, &state, ICE_SID_FLD_VEC_SW,
38762306a36Sopenharmony_ci					&offset, ice_sw_fv_handler);
38862306a36Sopenharmony_ci		if (!fv)
38962306a36Sopenharmony_ci			break;
39062306a36Sopenharmony_ci		ice_seg = NULL;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci		/* in the profile that not be used, the prot_id is set to 0xff
39362306a36Sopenharmony_ci		 * and the off is set to 0x1ff for all the field vectors.
39462306a36Sopenharmony_ci		 */
39562306a36Sopenharmony_ci		for (j = 0; j < hw->blk[ICE_BLK_SW].es.fvw; j++)
39662306a36Sopenharmony_ci			if (fv->ew[j].prot_id != ICE_PROT_INVALID ||
39762306a36Sopenharmony_ci			    fv->ew[j].off != ICE_FV_OFFSET_INVAL)
39862306a36Sopenharmony_ci				flag = true;
39962306a36Sopenharmony_ci		if (flag && prof_index > max_prof_index)
40062306a36Sopenharmony_ci			max_prof_index = prof_index;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci		prof_index++;
40362306a36Sopenharmony_ci		flag = false;
40462306a36Sopenharmony_ci	} while (fv);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	hw->switch_info->max_used_prof_index = max_prof_index;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	return 0;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci/**
41262306a36Sopenharmony_ci * ice_get_ddp_pkg_state - get DDP pkg state after download
41362306a36Sopenharmony_ci * @hw: pointer to the HW struct
41462306a36Sopenharmony_ci * @already_loaded: indicates if pkg was already loaded onto the device
41562306a36Sopenharmony_ci */
41662306a36Sopenharmony_cistatic enum ice_ddp_state ice_get_ddp_pkg_state(struct ice_hw *hw,
41762306a36Sopenharmony_ci						bool already_loaded)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	if (hw->pkg_ver.major == hw->active_pkg_ver.major &&
42062306a36Sopenharmony_ci	    hw->pkg_ver.minor == hw->active_pkg_ver.minor &&
42162306a36Sopenharmony_ci	    hw->pkg_ver.update == hw->active_pkg_ver.update &&
42262306a36Sopenharmony_ci	    hw->pkg_ver.draft == hw->active_pkg_ver.draft &&
42362306a36Sopenharmony_ci	    !memcmp(hw->pkg_name, hw->active_pkg_name, sizeof(hw->pkg_name))) {
42462306a36Sopenharmony_ci		if (already_loaded)
42562306a36Sopenharmony_ci			return ICE_DDP_PKG_SAME_VERSION_ALREADY_LOADED;
42662306a36Sopenharmony_ci		else
42762306a36Sopenharmony_ci			return ICE_DDP_PKG_SUCCESS;
42862306a36Sopenharmony_ci	} else if (hw->active_pkg_ver.major != ICE_PKG_SUPP_VER_MAJ ||
42962306a36Sopenharmony_ci		   hw->active_pkg_ver.minor != ICE_PKG_SUPP_VER_MNR) {
43062306a36Sopenharmony_ci		return ICE_DDP_PKG_ALREADY_LOADED_NOT_SUPPORTED;
43162306a36Sopenharmony_ci	} else if (hw->active_pkg_ver.major == ICE_PKG_SUPP_VER_MAJ &&
43262306a36Sopenharmony_ci		   hw->active_pkg_ver.minor == ICE_PKG_SUPP_VER_MNR) {
43362306a36Sopenharmony_ci		return ICE_DDP_PKG_COMPATIBLE_ALREADY_LOADED;
43462306a36Sopenharmony_ci	} else {
43562306a36Sopenharmony_ci		return ICE_DDP_PKG_ERR;
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci/**
44062306a36Sopenharmony_ci * ice_init_pkg_regs - initialize additional package registers
44162306a36Sopenharmony_ci * @hw: pointer to the hardware structure
44262306a36Sopenharmony_ci */
44362306a36Sopenharmony_cistatic void ice_init_pkg_regs(struct ice_hw *hw)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci#define ICE_SW_BLK_INP_MASK_L 0xFFFFFFFF
44662306a36Sopenharmony_ci#define ICE_SW_BLK_INP_MASK_H 0x0000FFFF
44762306a36Sopenharmony_ci#define ICE_SW_BLK_IDX 0
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	/* setup Switch block input mask, which is 48-bits in two parts */
45062306a36Sopenharmony_ci	wr32(hw, GL_PREEXT_L2_PMASK0(ICE_SW_BLK_IDX), ICE_SW_BLK_INP_MASK_L);
45162306a36Sopenharmony_ci	wr32(hw, GL_PREEXT_L2_PMASK1(ICE_SW_BLK_IDX), ICE_SW_BLK_INP_MASK_H);
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci/**
45562306a36Sopenharmony_ci * ice_marker_ptype_tcam_handler
45662306a36Sopenharmony_ci * @sect_type: section type
45762306a36Sopenharmony_ci * @section: pointer to section
45862306a36Sopenharmony_ci * @index: index of the Marker PType TCAM entry to be returned
45962306a36Sopenharmony_ci * @offset: pointer to receive absolute offset, always 0 for ptype TCAM sections
46062306a36Sopenharmony_ci *
46162306a36Sopenharmony_ci * This is a callback function that can be passed to ice_pkg_enum_entry.
46262306a36Sopenharmony_ci * Handles enumeration of individual Marker PType TCAM entries.
46362306a36Sopenharmony_ci */
46462306a36Sopenharmony_cistatic void *ice_marker_ptype_tcam_handler(u32 sect_type, void *section,
46562306a36Sopenharmony_ci					   u32 index, u32 *offset)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	struct ice_marker_ptype_tcam_section *marker_ptype;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	if (sect_type != ICE_SID_RXPARSER_MARKER_PTYPE)
47062306a36Sopenharmony_ci		return NULL;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	if (index > ICE_MAX_MARKER_PTYPE_TCAMS_IN_BUF)
47362306a36Sopenharmony_ci		return NULL;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	if (offset)
47662306a36Sopenharmony_ci		*offset = 0;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	marker_ptype = section;
47962306a36Sopenharmony_ci	if (index >= le16_to_cpu(marker_ptype->count))
48062306a36Sopenharmony_ci		return NULL;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	return marker_ptype->tcam + index;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci/**
48662306a36Sopenharmony_ci * ice_add_dvm_hint
48762306a36Sopenharmony_ci * @hw: pointer to the HW structure
48862306a36Sopenharmony_ci * @val: value of the boost entry
48962306a36Sopenharmony_ci * @enable: true if entry needs to be enabled, or false if needs to be disabled
49062306a36Sopenharmony_ci */
49162306a36Sopenharmony_cistatic void ice_add_dvm_hint(struct ice_hw *hw, u16 val, bool enable)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	if (hw->dvm_upd.count < ICE_DVM_MAX_ENTRIES) {
49462306a36Sopenharmony_ci		hw->dvm_upd.tbl[hw->dvm_upd.count].boost_addr = val;
49562306a36Sopenharmony_ci		hw->dvm_upd.tbl[hw->dvm_upd.count].enable = enable;
49662306a36Sopenharmony_ci		hw->dvm_upd.count++;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci/**
50162306a36Sopenharmony_ci * ice_add_tunnel_hint
50262306a36Sopenharmony_ci * @hw: pointer to the HW structure
50362306a36Sopenharmony_ci * @label_name: label text
50462306a36Sopenharmony_ci * @val: value of the tunnel port boost entry
50562306a36Sopenharmony_ci */
50662306a36Sopenharmony_cistatic void ice_add_tunnel_hint(struct ice_hw *hw, char *label_name, u16 val)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	if (hw->tnl.count < ICE_TUNNEL_MAX_ENTRIES) {
50962306a36Sopenharmony_ci		u16 i;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci		for (i = 0; tnls[i].type != TNL_LAST; i++) {
51262306a36Sopenharmony_ci			size_t len = strlen(tnls[i].label_prefix);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci			/* Look for matching label start, before continuing */
51562306a36Sopenharmony_ci			if (strncmp(label_name, tnls[i].label_prefix, len))
51662306a36Sopenharmony_ci				continue;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci			/* Make sure this label matches our PF. Note that the PF
51962306a36Sopenharmony_ci			 * character ('0' - '7') will be located where our
52062306a36Sopenharmony_ci			 * prefix string's null terminator is located.
52162306a36Sopenharmony_ci			 */
52262306a36Sopenharmony_ci			if ((label_name[len] - '0') == hw->pf_id) {
52362306a36Sopenharmony_ci				hw->tnl.tbl[hw->tnl.count].type = tnls[i].type;
52462306a36Sopenharmony_ci				hw->tnl.tbl[hw->tnl.count].valid = false;
52562306a36Sopenharmony_ci				hw->tnl.tbl[hw->tnl.count].boost_addr = val;
52662306a36Sopenharmony_ci				hw->tnl.tbl[hw->tnl.count].port = 0;
52762306a36Sopenharmony_ci				hw->tnl.count++;
52862306a36Sopenharmony_ci				break;
52962306a36Sopenharmony_ci			}
53062306a36Sopenharmony_ci		}
53162306a36Sopenharmony_ci	}
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci/**
53562306a36Sopenharmony_ci * ice_label_enum_handler
53662306a36Sopenharmony_ci * @sect_type: section type
53762306a36Sopenharmony_ci * @section: pointer to section
53862306a36Sopenharmony_ci * @index: index of the label entry to be returned
53962306a36Sopenharmony_ci * @offset: pointer to receive absolute offset, always zero for label sections
54062306a36Sopenharmony_ci *
54162306a36Sopenharmony_ci * This is a callback function that can be passed to ice_pkg_enum_entry.
54262306a36Sopenharmony_ci * Handles enumeration of individual label entries.
54362306a36Sopenharmony_ci */
54462306a36Sopenharmony_cistatic void *ice_label_enum_handler(u32 __always_unused sect_type,
54562306a36Sopenharmony_ci				    void *section, u32 index, u32 *offset)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	struct ice_label_section *labels;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	if (!section)
55062306a36Sopenharmony_ci		return NULL;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	if (index > ICE_MAX_LABELS_IN_BUF)
55362306a36Sopenharmony_ci		return NULL;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	if (offset)
55662306a36Sopenharmony_ci		*offset = 0;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	labels = section;
55962306a36Sopenharmony_ci	if (index >= le16_to_cpu(labels->count))
56062306a36Sopenharmony_ci		return NULL;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	return labels->label + index;
56362306a36Sopenharmony_ci}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci/**
56662306a36Sopenharmony_ci * ice_enum_labels
56762306a36Sopenharmony_ci * @ice_seg: pointer to the ice segment (NULL on subsequent calls)
56862306a36Sopenharmony_ci * @type: the section type that will contain the label (0 on subsequent calls)
56962306a36Sopenharmony_ci * @state: ice_pkg_enum structure that will hold the state of the enumeration
57062306a36Sopenharmony_ci * @value: pointer to a value that will return the label's value if found
57162306a36Sopenharmony_ci *
57262306a36Sopenharmony_ci * Enumerates a list of labels in the package. The caller will call
57362306a36Sopenharmony_ci * ice_enum_labels(ice_seg, type, ...) to start the enumeration, then call
57462306a36Sopenharmony_ci * ice_enum_labels(NULL, 0, ...) to continue. When the function returns a NULL
57562306a36Sopenharmony_ci * the end of the list has been reached.
57662306a36Sopenharmony_ci */
57762306a36Sopenharmony_cistatic char *ice_enum_labels(struct ice_seg *ice_seg, u32 type,
57862306a36Sopenharmony_ci			     struct ice_pkg_enum *state, u16 *value)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	struct ice_label *label;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	/* Check for valid label section on first call */
58362306a36Sopenharmony_ci	if (type && !(type >= ICE_SID_LBL_FIRST && type <= ICE_SID_LBL_LAST))
58462306a36Sopenharmony_ci		return NULL;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	label = ice_pkg_enum_entry(ice_seg, state, type, NULL,
58762306a36Sopenharmony_ci				   ice_label_enum_handler);
58862306a36Sopenharmony_ci	if (!label)
58962306a36Sopenharmony_ci		return NULL;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	*value = le16_to_cpu(label->value);
59262306a36Sopenharmony_ci	return label->name;
59362306a36Sopenharmony_ci}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci/**
59662306a36Sopenharmony_ci * ice_boost_tcam_handler
59762306a36Sopenharmony_ci * @sect_type: section type
59862306a36Sopenharmony_ci * @section: pointer to section
59962306a36Sopenharmony_ci * @index: index of the boost TCAM entry to be returned
60062306a36Sopenharmony_ci * @offset: pointer to receive absolute offset, always 0 for boost TCAM sections
60162306a36Sopenharmony_ci *
60262306a36Sopenharmony_ci * This is a callback function that can be passed to ice_pkg_enum_entry.
60362306a36Sopenharmony_ci * Handles enumeration of individual boost TCAM entries.
60462306a36Sopenharmony_ci */
60562306a36Sopenharmony_cistatic void *ice_boost_tcam_handler(u32 sect_type, void *section, u32 index,
60662306a36Sopenharmony_ci				    u32 *offset)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	struct ice_boost_tcam_section *boost;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	if (!section)
61162306a36Sopenharmony_ci		return NULL;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	if (sect_type != ICE_SID_RXPARSER_BOOST_TCAM)
61462306a36Sopenharmony_ci		return NULL;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if (index > ICE_MAX_BST_TCAMS_IN_BUF)
61762306a36Sopenharmony_ci		return NULL;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	if (offset)
62062306a36Sopenharmony_ci		*offset = 0;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	boost = section;
62362306a36Sopenharmony_ci	if (index >= le16_to_cpu(boost->count))
62462306a36Sopenharmony_ci		return NULL;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	return boost->tcam + index;
62762306a36Sopenharmony_ci}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci/**
63062306a36Sopenharmony_ci * ice_find_boost_entry
63162306a36Sopenharmony_ci * @ice_seg: pointer to the ice segment (non-NULL)
63262306a36Sopenharmony_ci * @addr: Boost TCAM address of entry to search for
63362306a36Sopenharmony_ci * @entry: returns pointer to the entry
63462306a36Sopenharmony_ci *
63562306a36Sopenharmony_ci * Finds a particular Boost TCAM entry and returns a pointer to that entry
63662306a36Sopenharmony_ci * if it is found. The ice_seg parameter must not be NULL since the first call
63762306a36Sopenharmony_ci * to ice_pkg_enum_entry requires a pointer to an actual ice_segment structure.
63862306a36Sopenharmony_ci */
63962306a36Sopenharmony_cistatic int ice_find_boost_entry(struct ice_seg *ice_seg, u16 addr,
64062306a36Sopenharmony_ci				struct ice_boost_tcam_entry **entry)
64162306a36Sopenharmony_ci{
64262306a36Sopenharmony_ci	struct ice_boost_tcam_entry *tcam;
64362306a36Sopenharmony_ci	struct ice_pkg_enum state;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	memset(&state, 0, sizeof(state));
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	if (!ice_seg)
64862306a36Sopenharmony_ci		return -EINVAL;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	do {
65162306a36Sopenharmony_ci		tcam = ice_pkg_enum_entry(ice_seg, &state,
65262306a36Sopenharmony_ci					  ICE_SID_RXPARSER_BOOST_TCAM, NULL,
65362306a36Sopenharmony_ci					  ice_boost_tcam_handler);
65462306a36Sopenharmony_ci		if (tcam && le16_to_cpu(tcam->addr) == addr) {
65562306a36Sopenharmony_ci			*entry = tcam;
65662306a36Sopenharmony_ci			return 0;
65762306a36Sopenharmony_ci		}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci		ice_seg = NULL;
66062306a36Sopenharmony_ci	} while (tcam);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	*entry = NULL;
66362306a36Sopenharmony_ci	return -EIO;
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci/**
66762306a36Sopenharmony_ci * ice_is_init_pkg_successful - check if DDP init was successful
66862306a36Sopenharmony_ci * @state: state of the DDP pkg after download
66962306a36Sopenharmony_ci */
67062306a36Sopenharmony_cibool ice_is_init_pkg_successful(enum ice_ddp_state state)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	switch (state) {
67362306a36Sopenharmony_ci	case ICE_DDP_PKG_SUCCESS:
67462306a36Sopenharmony_ci	case ICE_DDP_PKG_SAME_VERSION_ALREADY_LOADED:
67562306a36Sopenharmony_ci	case ICE_DDP_PKG_COMPATIBLE_ALREADY_LOADED:
67662306a36Sopenharmony_ci		return true;
67762306a36Sopenharmony_ci	default:
67862306a36Sopenharmony_ci		return false;
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci/**
68362306a36Sopenharmony_ci * ice_pkg_buf_alloc
68462306a36Sopenharmony_ci * @hw: pointer to the HW structure
68562306a36Sopenharmony_ci *
68662306a36Sopenharmony_ci * Allocates a package buffer and returns a pointer to the buffer header.
68762306a36Sopenharmony_ci * Note: all package contents must be in Little Endian form.
68862306a36Sopenharmony_ci */
68962306a36Sopenharmony_cistruct ice_buf_build *ice_pkg_buf_alloc(struct ice_hw *hw)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci	struct ice_buf_build *bld;
69262306a36Sopenharmony_ci	struct ice_buf_hdr *buf;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	bld = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*bld), GFP_KERNEL);
69562306a36Sopenharmony_ci	if (!bld)
69662306a36Sopenharmony_ci		return NULL;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	buf = (struct ice_buf_hdr *)bld;
69962306a36Sopenharmony_ci	buf->data_end =
70062306a36Sopenharmony_ci		cpu_to_le16(offsetof(struct ice_buf_hdr, section_entry));
70162306a36Sopenharmony_ci	return bld;
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic bool ice_is_gtp_u_profile(u16 prof_idx)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	return (prof_idx >= ICE_PROFID_IPV6_GTPU_TEID &&
70762306a36Sopenharmony_ci		prof_idx <= ICE_PROFID_IPV6_GTPU_IPV6_TCP_INNER) ||
70862306a36Sopenharmony_ci	       prof_idx == ICE_PROFID_IPV4_GTPU_TEID;
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic bool ice_is_gtp_c_profile(u16 prof_idx)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	switch (prof_idx) {
71462306a36Sopenharmony_ci	case ICE_PROFID_IPV4_GTPC_TEID:
71562306a36Sopenharmony_ci	case ICE_PROFID_IPV4_GTPC_NO_TEID:
71662306a36Sopenharmony_ci	case ICE_PROFID_IPV6_GTPC_TEID:
71762306a36Sopenharmony_ci	case ICE_PROFID_IPV6_GTPC_NO_TEID:
71862306a36Sopenharmony_ci		return true;
71962306a36Sopenharmony_ci	default:
72062306a36Sopenharmony_ci		return false;
72162306a36Sopenharmony_ci	}
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci/**
72562306a36Sopenharmony_ci * ice_get_sw_prof_type - determine switch profile type
72662306a36Sopenharmony_ci * @hw: pointer to the HW structure
72762306a36Sopenharmony_ci * @fv: pointer to the switch field vector
72862306a36Sopenharmony_ci * @prof_idx: profile index to check
72962306a36Sopenharmony_ci */
73062306a36Sopenharmony_cistatic enum ice_prof_type ice_get_sw_prof_type(struct ice_hw *hw,
73162306a36Sopenharmony_ci					       struct ice_fv *fv, u32 prof_idx)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	u16 i;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	if (ice_is_gtp_c_profile(prof_idx))
73662306a36Sopenharmony_ci		return ICE_PROF_TUN_GTPC;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	if (ice_is_gtp_u_profile(prof_idx))
73962306a36Sopenharmony_ci		return ICE_PROF_TUN_GTPU;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	for (i = 0; i < hw->blk[ICE_BLK_SW].es.fvw; i++) {
74262306a36Sopenharmony_ci		/* UDP tunnel will have UDP_OF protocol ID and VNI offset */
74362306a36Sopenharmony_ci		if (fv->ew[i].prot_id == (u8)ICE_PROT_UDP_OF &&
74462306a36Sopenharmony_ci		    fv->ew[i].off == ICE_VNI_OFFSET)
74562306a36Sopenharmony_ci			return ICE_PROF_TUN_UDP;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci		/* GRE tunnel will have GRE protocol */
74862306a36Sopenharmony_ci		if (fv->ew[i].prot_id == (u8)ICE_PROT_GRE_OF)
74962306a36Sopenharmony_ci			return ICE_PROF_TUN_GRE;
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	return ICE_PROF_NON_TUN;
75362306a36Sopenharmony_ci}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci/**
75662306a36Sopenharmony_ci * ice_get_sw_fv_bitmap - Get switch field vector bitmap based on profile type
75762306a36Sopenharmony_ci * @hw: pointer to hardware structure
75862306a36Sopenharmony_ci * @req_profs: type of profiles requested
75962306a36Sopenharmony_ci * @bm: pointer to memory for returning the bitmap of field vectors
76062306a36Sopenharmony_ci */
76162306a36Sopenharmony_civoid ice_get_sw_fv_bitmap(struct ice_hw *hw, enum ice_prof_type req_profs,
76262306a36Sopenharmony_ci			  unsigned long *bm)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	struct ice_pkg_enum state;
76562306a36Sopenharmony_ci	struct ice_seg *ice_seg;
76662306a36Sopenharmony_ci	struct ice_fv *fv;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	if (req_profs == ICE_PROF_ALL) {
76962306a36Sopenharmony_ci		bitmap_set(bm, 0, ICE_MAX_NUM_PROFILES);
77062306a36Sopenharmony_ci		return;
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	memset(&state, 0, sizeof(state));
77462306a36Sopenharmony_ci	bitmap_zero(bm, ICE_MAX_NUM_PROFILES);
77562306a36Sopenharmony_ci	ice_seg = hw->seg;
77662306a36Sopenharmony_ci	do {
77762306a36Sopenharmony_ci		enum ice_prof_type prof_type;
77862306a36Sopenharmony_ci		u32 offset;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci		fv = ice_pkg_enum_entry(ice_seg, &state, ICE_SID_FLD_VEC_SW,
78162306a36Sopenharmony_ci					&offset, ice_sw_fv_handler);
78262306a36Sopenharmony_ci		ice_seg = NULL;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		if (fv) {
78562306a36Sopenharmony_ci			/* Determine field vector type */
78662306a36Sopenharmony_ci			prof_type = ice_get_sw_prof_type(hw, fv, offset);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci			if (req_profs & prof_type)
78962306a36Sopenharmony_ci				set_bit((u16)offset, bm);
79062306a36Sopenharmony_ci		}
79162306a36Sopenharmony_ci	} while (fv);
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci/**
79562306a36Sopenharmony_ci * ice_get_sw_fv_list
79662306a36Sopenharmony_ci * @hw: pointer to the HW structure
79762306a36Sopenharmony_ci * @lkups: list of protocol types
79862306a36Sopenharmony_ci * @bm: bitmap of field vectors to consider
79962306a36Sopenharmony_ci * @fv_list: Head of a list
80062306a36Sopenharmony_ci *
80162306a36Sopenharmony_ci * Finds all the field vector entries from switch block that contain
80262306a36Sopenharmony_ci * a given protocol ID and offset and returns a list of structures of type
80362306a36Sopenharmony_ci * "ice_sw_fv_list_entry". Every structure in the list has a field vector
80462306a36Sopenharmony_ci * definition and profile ID information
80562306a36Sopenharmony_ci * NOTE: The caller of the function is responsible for freeing the memory
80662306a36Sopenharmony_ci * allocated for every list entry.
80762306a36Sopenharmony_ci */
80862306a36Sopenharmony_ciint ice_get_sw_fv_list(struct ice_hw *hw, struct ice_prot_lkup_ext *lkups,
80962306a36Sopenharmony_ci		       unsigned long *bm, struct list_head *fv_list)
81062306a36Sopenharmony_ci{
81162306a36Sopenharmony_ci	struct ice_sw_fv_list_entry *fvl;
81262306a36Sopenharmony_ci	struct ice_sw_fv_list_entry *tmp;
81362306a36Sopenharmony_ci	struct ice_pkg_enum state;
81462306a36Sopenharmony_ci	struct ice_seg *ice_seg;
81562306a36Sopenharmony_ci	struct ice_fv *fv;
81662306a36Sopenharmony_ci	u32 offset;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	memset(&state, 0, sizeof(state));
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	if (!lkups->n_val_words || !hw->seg)
82162306a36Sopenharmony_ci		return -EINVAL;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	ice_seg = hw->seg;
82462306a36Sopenharmony_ci	do {
82562306a36Sopenharmony_ci		u16 i;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci		fv = ice_pkg_enum_entry(ice_seg, &state, ICE_SID_FLD_VEC_SW,
82862306a36Sopenharmony_ci					&offset, ice_sw_fv_handler);
82962306a36Sopenharmony_ci		if (!fv)
83062306a36Sopenharmony_ci			break;
83162306a36Sopenharmony_ci		ice_seg = NULL;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci		/* If field vector is not in the bitmap list, then skip this
83462306a36Sopenharmony_ci		 * profile.
83562306a36Sopenharmony_ci		 */
83662306a36Sopenharmony_ci		if (!test_bit((u16)offset, bm))
83762306a36Sopenharmony_ci			continue;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci		for (i = 0; i < lkups->n_val_words; i++) {
84062306a36Sopenharmony_ci			int j;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci			for (j = 0; j < hw->blk[ICE_BLK_SW].es.fvw; j++)
84362306a36Sopenharmony_ci				if (fv->ew[j].prot_id ==
84462306a36Sopenharmony_ci					    lkups->fv_words[i].prot_id &&
84562306a36Sopenharmony_ci				    fv->ew[j].off == lkups->fv_words[i].off)
84662306a36Sopenharmony_ci					break;
84762306a36Sopenharmony_ci			if (j >= hw->blk[ICE_BLK_SW].es.fvw)
84862306a36Sopenharmony_ci				break;
84962306a36Sopenharmony_ci			if (i + 1 == lkups->n_val_words) {
85062306a36Sopenharmony_ci				fvl = devm_kzalloc(ice_hw_to_dev(hw),
85162306a36Sopenharmony_ci						   sizeof(*fvl), GFP_KERNEL);
85262306a36Sopenharmony_ci				if (!fvl)
85362306a36Sopenharmony_ci					goto err;
85462306a36Sopenharmony_ci				fvl->fv_ptr = fv;
85562306a36Sopenharmony_ci				fvl->profile_id = offset;
85662306a36Sopenharmony_ci				list_add(&fvl->list_entry, fv_list);
85762306a36Sopenharmony_ci				break;
85862306a36Sopenharmony_ci			}
85962306a36Sopenharmony_ci		}
86062306a36Sopenharmony_ci	} while (fv);
86162306a36Sopenharmony_ci	if (list_empty(fv_list)) {
86262306a36Sopenharmony_ci		dev_warn(ice_hw_to_dev(hw),
86362306a36Sopenharmony_ci			 "Required profiles not found in currently loaded DDP package");
86462306a36Sopenharmony_ci		return -EIO;
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	return 0;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_cierr:
87062306a36Sopenharmony_ci	list_for_each_entry_safe(fvl, tmp, fv_list, list_entry) {
87162306a36Sopenharmony_ci		list_del(&fvl->list_entry);
87262306a36Sopenharmony_ci		devm_kfree(ice_hw_to_dev(hw), fvl);
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	return -ENOMEM;
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci/**
87962306a36Sopenharmony_ci * ice_init_prof_result_bm - Initialize the profile result index bitmap
88062306a36Sopenharmony_ci * @hw: pointer to hardware structure
88162306a36Sopenharmony_ci */
88262306a36Sopenharmony_civoid ice_init_prof_result_bm(struct ice_hw *hw)
88362306a36Sopenharmony_ci{
88462306a36Sopenharmony_ci	struct ice_pkg_enum state;
88562306a36Sopenharmony_ci	struct ice_seg *ice_seg;
88662306a36Sopenharmony_ci	struct ice_fv *fv;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	memset(&state, 0, sizeof(state));
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	if (!hw->seg)
89162306a36Sopenharmony_ci		return;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	ice_seg = hw->seg;
89462306a36Sopenharmony_ci	do {
89562306a36Sopenharmony_ci		u32 off;
89662306a36Sopenharmony_ci		u16 i;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci		fv = ice_pkg_enum_entry(ice_seg, &state, ICE_SID_FLD_VEC_SW,
89962306a36Sopenharmony_ci					&off, ice_sw_fv_handler);
90062306a36Sopenharmony_ci		ice_seg = NULL;
90162306a36Sopenharmony_ci		if (!fv)
90262306a36Sopenharmony_ci			break;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci		bitmap_zero(hw->switch_info->prof_res_bm[off],
90562306a36Sopenharmony_ci			    ICE_MAX_FV_WORDS);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci		/* Determine empty field vector indices, these can be
90862306a36Sopenharmony_ci		 * used for recipe results. Skip index 0, since it is
90962306a36Sopenharmony_ci		 * always used for Switch ID.
91062306a36Sopenharmony_ci		 */
91162306a36Sopenharmony_ci		for (i = 1; i < ICE_MAX_FV_WORDS; i++)
91262306a36Sopenharmony_ci			if (fv->ew[i].prot_id == ICE_PROT_INVALID &&
91362306a36Sopenharmony_ci			    fv->ew[i].off == ICE_FV_OFFSET_INVAL)
91462306a36Sopenharmony_ci				set_bit(i, hw->switch_info->prof_res_bm[off]);
91562306a36Sopenharmony_ci	} while (fv);
91662306a36Sopenharmony_ci}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci/**
91962306a36Sopenharmony_ci * ice_pkg_buf_free
92062306a36Sopenharmony_ci * @hw: pointer to the HW structure
92162306a36Sopenharmony_ci * @bld: pointer to pkg build (allocated by ice_pkg_buf_alloc())
92262306a36Sopenharmony_ci *
92362306a36Sopenharmony_ci * Frees a package buffer
92462306a36Sopenharmony_ci */
92562306a36Sopenharmony_civoid ice_pkg_buf_free(struct ice_hw *hw, struct ice_buf_build *bld)
92662306a36Sopenharmony_ci{
92762306a36Sopenharmony_ci	devm_kfree(ice_hw_to_dev(hw), bld);
92862306a36Sopenharmony_ci}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci/**
93162306a36Sopenharmony_ci * ice_pkg_buf_reserve_section
93262306a36Sopenharmony_ci * @bld: pointer to pkg build (allocated by ice_pkg_buf_alloc())
93362306a36Sopenharmony_ci * @count: the number of sections to reserve
93462306a36Sopenharmony_ci *
93562306a36Sopenharmony_ci * Reserves one or more section table entries in a package buffer. This routine
93662306a36Sopenharmony_ci * can be called multiple times as long as they are made before calling
93762306a36Sopenharmony_ci * ice_pkg_buf_alloc_section(). Once ice_pkg_buf_alloc_section()
93862306a36Sopenharmony_ci * is called once, the number of sections that can be allocated will not be able
93962306a36Sopenharmony_ci * to be increased; not using all reserved sections is fine, but this will
94062306a36Sopenharmony_ci * result in some wasted space in the buffer.
94162306a36Sopenharmony_ci * Note: all package contents must be in Little Endian form.
94262306a36Sopenharmony_ci */
94362306a36Sopenharmony_ciint ice_pkg_buf_reserve_section(struct ice_buf_build *bld, u16 count)
94462306a36Sopenharmony_ci{
94562306a36Sopenharmony_ci	struct ice_buf_hdr *buf;
94662306a36Sopenharmony_ci	u16 section_count;
94762306a36Sopenharmony_ci	u16 data_end;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	if (!bld)
95062306a36Sopenharmony_ci		return -EINVAL;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	buf = (struct ice_buf_hdr *)&bld->buf;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	/* already an active section, can't increase table size */
95562306a36Sopenharmony_ci	section_count = le16_to_cpu(buf->section_count);
95662306a36Sopenharmony_ci	if (section_count > 0)
95762306a36Sopenharmony_ci		return -EIO;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	if (bld->reserved_section_table_entries + count > ICE_MAX_S_COUNT)
96062306a36Sopenharmony_ci		return -EIO;
96162306a36Sopenharmony_ci	bld->reserved_section_table_entries += count;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	data_end = le16_to_cpu(buf->data_end) +
96462306a36Sopenharmony_ci		   flex_array_size(buf, section_entry, count);
96562306a36Sopenharmony_ci	buf->data_end = cpu_to_le16(data_end);
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	return 0;
96862306a36Sopenharmony_ci}
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci/**
97162306a36Sopenharmony_ci * ice_pkg_buf_alloc_section
97262306a36Sopenharmony_ci * @bld: pointer to pkg build (allocated by ice_pkg_buf_alloc())
97362306a36Sopenharmony_ci * @type: the section type value
97462306a36Sopenharmony_ci * @size: the size of the section to reserve (in bytes)
97562306a36Sopenharmony_ci *
97662306a36Sopenharmony_ci * Reserves memory in the buffer for a section's content and updates the
97762306a36Sopenharmony_ci * buffers' status accordingly. This routine returns a pointer to the first
97862306a36Sopenharmony_ci * byte of the section start within the buffer, which is used to fill in the
97962306a36Sopenharmony_ci * section contents.
98062306a36Sopenharmony_ci * Note: all package contents must be in Little Endian form.
98162306a36Sopenharmony_ci */
98262306a36Sopenharmony_civoid *ice_pkg_buf_alloc_section(struct ice_buf_build *bld, u32 type, u16 size)
98362306a36Sopenharmony_ci{
98462306a36Sopenharmony_ci	struct ice_buf_hdr *buf;
98562306a36Sopenharmony_ci	u16 sect_count;
98662306a36Sopenharmony_ci	u16 data_end;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	if (!bld || !type || !size)
98962306a36Sopenharmony_ci		return NULL;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	buf = (struct ice_buf_hdr *)&bld->buf;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	/* check for enough space left in buffer */
99462306a36Sopenharmony_ci	data_end = le16_to_cpu(buf->data_end);
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	/* section start must align on 4 byte boundary */
99762306a36Sopenharmony_ci	data_end = ALIGN(data_end, 4);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	if ((data_end + size) > ICE_MAX_S_DATA_END)
100062306a36Sopenharmony_ci		return NULL;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	/* check for more available section table entries */
100362306a36Sopenharmony_ci	sect_count = le16_to_cpu(buf->section_count);
100462306a36Sopenharmony_ci	if (sect_count < bld->reserved_section_table_entries) {
100562306a36Sopenharmony_ci		void *section_ptr = ((u8 *)buf) + data_end;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci		buf->section_entry[sect_count].offset = cpu_to_le16(data_end);
100862306a36Sopenharmony_ci		buf->section_entry[sect_count].size = cpu_to_le16(size);
100962306a36Sopenharmony_ci		buf->section_entry[sect_count].type = cpu_to_le32(type);
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci		data_end += size;
101262306a36Sopenharmony_ci		buf->data_end = cpu_to_le16(data_end);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci		buf->section_count = cpu_to_le16(sect_count + 1);
101562306a36Sopenharmony_ci		return section_ptr;
101662306a36Sopenharmony_ci	}
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	/* no free section table entries */
101962306a36Sopenharmony_ci	return NULL;
102062306a36Sopenharmony_ci}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci/**
102362306a36Sopenharmony_ci * ice_pkg_buf_alloc_single_section
102462306a36Sopenharmony_ci * @hw: pointer to the HW structure
102562306a36Sopenharmony_ci * @type: the section type value
102662306a36Sopenharmony_ci * @size: the size of the section to reserve (in bytes)
102762306a36Sopenharmony_ci * @section: returns pointer to the section
102862306a36Sopenharmony_ci *
102962306a36Sopenharmony_ci * Allocates a package buffer with a single section.
103062306a36Sopenharmony_ci * Note: all package contents must be in Little Endian form.
103162306a36Sopenharmony_ci */
103262306a36Sopenharmony_cistruct ice_buf_build *ice_pkg_buf_alloc_single_section(struct ice_hw *hw,
103362306a36Sopenharmony_ci						       u32 type, u16 size,
103462306a36Sopenharmony_ci						       void **section)
103562306a36Sopenharmony_ci{
103662306a36Sopenharmony_ci	struct ice_buf_build *buf;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	if (!section)
103962306a36Sopenharmony_ci		return NULL;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	buf = ice_pkg_buf_alloc(hw);
104262306a36Sopenharmony_ci	if (!buf)
104362306a36Sopenharmony_ci		return NULL;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	if (ice_pkg_buf_reserve_section(buf, 1))
104662306a36Sopenharmony_ci		goto ice_pkg_buf_alloc_single_section_err;
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	*section = ice_pkg_buf_alloc_section(buf, type, size);
104962306a36Sopenharmony_ci	if (!*section)
105062306a36Sopenharmony_ci		goto ice_pkg_buf_alloc_single_section_err;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	return buf;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ciice_pkg_buf_alloc_single_section_err:
105562306a36Sopenharmony_ci	ice_pkg_buf_free(hw, buf);
105662306a36Sopenharmony_ci	return NULL;
105762306a36Sopenharmony_ci}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci/**
106062306a36Sopenharmony_ci * ice_pkg_buf_get_active_sections
106162306a36Sopenharmony_ci * @bld: pointer to pkg build (allocated by ice_pkg_buf_alloc())
106262306a36Sopenharmony_ci *
106362306a36Sopenharmony_ci * Returns the number of active sections. Before using the package buffer
106462306a36Sopenharmony_ci * in an update package command, the caller should make sure that there is at
106562306a36Sopenharmony_ci * least one active section - otherwise, the buffer is not legal and should
106662306a36Sopenharmony_ci * not be used.
106762306a36Sopenharmony_ci * Note: all package contents must be in Little Endian form.
106862306a36Sopenharmony_ci */
106962306a36Sopenharmony_ciu16 ice_pkg_buf_get_active_sections(struct ice_buf_build *bld)
107062306a36Sopenharmony_ci{
107162306a36Sopenharmony_ci	struct ice_buf_hdr *buf;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	if (!bld)
107462306a36Sopenharmony_ci		return 0;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	buf = (struct ice_buf_hdr *)&bld->buf;
107762306a36Sopenharmony_ci	return le16_to_cpu(buf->section_count);
107862306a36Sopenharmony_ci}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci/**
108162306a36Sopenharmony_ci * ice_pkg_buf
108262306a36Sopenharmony_ci * @bld: pointer to pkg build (allocated by ice_pkg_buf_alloc())
108362306a36Sopenharmony_ci *
108462306a36Sopenharmony_ci * Return a pointer to the buffer's header
108562306a36Sopenharmony_ci */
108662306a36Sopenharmony_cistruct ice_buf *ice_pkg_buf(struct ice_buf_build *bld)
108762306a36Sopenharmony_ci{
108862306a36Sopenharmony_ci	if (!bld)
108962306a36Sopenharmony_ci		return NULL;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	return &bld->buf;
109262306a36Sopenharmony_ci}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_cistatic enum ice_ddp_state ice_map_aq_err_to_ddp_state(enum ice_aq_err aq_err)
109562306a36Sopenharmony_ci{
109662306a36Sopenharmony_ci	switch (aq_err) {
109762306a36Sopenharmony_ci	case ICE_AQ_RC_ENOSEC:
109862306a36Sopenharmony_ci	case ICE_AQ_RC_EBADSIG:
109962306a36Sopenharmony_ci		return ICE_DDP_PKG_FILE_SIGNATURE_INVALID;
110062306a36Sopenharmony_ci	case ICE_AQ_RC_ESVN:
110162306a36Sopenharmony_ci		return ICE_DDP_PKG_FILE_REVISION_TOO_LOW;
110262306a36Sopenharmony_ci	case ICE_AQ_RC_EBADMAN:
110362306a36Sopenharmony_ci	case ICE_AQ_RC_EBADBUF:
110462306a36Sopenharmony_ci		return ICE_DDP_PKG_LOAD_ERROR;
110562306a36Sopenharmony_ci	default:
110662306a36Sopenharmony_ci		return ICE_DDP_PKG_ERR;
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci/**
111162306a36Sopenharmony_ci * ice_acquire_global_cfg_lock
111262306a36Sopenharmony_ci * @hw: pointer to the HW structure
111362306a36Sopenharmony_ci * @access: access type (read or write)
111462306a36Sopenharmony_ci *
111562306a36Sopenharmony_ci * This function will request ownership of the global config lock for reading
111662306a36Sopenharmony_ci * or writing of the package. When attempting to obtain write access, the
111762306a36Sopenharmony_ci * caller must check for the following two return values:
111862306a36Sopenharmony_ci *
111962306a36Sopenharmony_ci * 0         -  Means the caller has acquired the global config lock
112062306a36Sopenharmony_ci *              and can perform writing of the package.
112162306a36Sopenharmony_ci * -EALREADY - Indicates another driver has already written the
112262306a36Sopenharmony_ci *             package or has found that no update was necessary; in
112362306a36Sopenharmony_ci *             this case, the caller can just skip performing any
112462306a36Sopenharmony_ci *             update of the package.
112562306a36Sopenharmony_ci */
112662306a36Sopenharmony_cistatic int ice_acquire_global_cfg_lock(struct ice_hw *hw,
112762306a36Sopenharmony_ci				       enum ice_aq_res_access_type access)
112862306a36Sopenharmony_ci{
112962306a36Sopenharmony_ci	int status;
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	status = ice_acquire_res(hw, ICE_GLOBAL_CFG_LOCK_RES_ID, access,
113262306a36Sopenharmony_ci				 ICE_GLOBAL_CFG_LOCK_TIMEOUT);
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	if (!status)
113562306a36Sopenharmony_ci		mutex_lock(&ice_global_cfg_lock_sw);
113662306a36Sopenharmony_ci	else if (status == -EALREADY)
113762306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_PKG,
113862306a36Sopenharmony_ci			  "Global config lock: No work to do\n");
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	return status;
114162306a36Sopenharmony_ci}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci/**
114462306a36Sopenharmony_ci * ice_release_global_cfg_lock
114562306a36Sopenharmony_ci * @hw: pointer to the HW structure
114662306a36Sopenharmony_ci *
114762306a36Sopenharmony_ci * This function will release the global config lock.
114862306a36Sopenharmony_ci */
114962306a36Sopenharmony_cistatic void ice_release_global_cfg_lock(struct ice_hw *hw)
115062306a36Sopenharmony_ci{
115162306a36Sopenharmony_ci	mutex_unlock(&ice_global_cfg_lock_sw);
115262306a36Sopenharmony_ci	ice_release_res(hw, ICE_GLOBAL_CFG_LOCK_RES_ID);
115362306a36Sopenharmony_ci}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci/**
115662306a36Sopenharmony_ci * ice_aq_download_pkg
115762306a36Sopenharmony_ci * @hw: pointer to the hardware structure
115862306a36Sopenharmony_ci * @pkg_buf: the package buffer to transfer
115962306a36Sopenharmony_ci * @buf_size: the size of the package buffer
116062306a36Sopenharmony_ci * @last_buf: last buffer indicator
116162306a36Sopenharmony_ci * @error_offset: returns error offset
116262306a36Sopenharmony_ci * @error_info: returns error information
116362306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
116462306a36Sopenharmony_ci *
116562306a36Sopenharmony_ci * Download Package (0x0C40)
116662306a36Sopenharmony_ci */
116762306a36Sopenharmony_cistatic int
116862306a36Sopenharmony_ciice_aq_download_pkg(struct ice_hw *hw, struct ice_buf_hdr *pkg_buf,
116962306a36Sopenharmony_ci		    u16 buf_size, bool last_buf, u32 *error_offset,
117062306a36Sopenharmony_ci		    u32 *error_info, struct ice_sq_cd *cd)
117162306a36Sopenharmony_ci{
117262306a36Sopenharmony_ci	struct ice_aqc_download_pkg *cmd;
117362306a36Sopenharmony_ci	struct ice_aq_desc desc;
117462306a36Sopenharmony_ci	int status;
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	if (error_offset)
117762306a36Sopenharmony_ci		*error_offset = 0;
117862306a36Sopenharmony_ci	if (error_info)
117962306a36Sopenharmony_ci		*error_info = 0;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	cmd = &desc.params.download_pkg;
118262306a36Sopenharmony_ci	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_download_pkg);
118362306a36Sopenharmony_ci	desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	if (last_buf)
118662306a36Sopenharmony_ci		cmd->flags |= ICE_AQC_DOWNLOAD_PKG_LAST_BUF;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	status = ice_aq_send_cmd(hw, &desc, pkg_buf, buf_size, cd);
118962306a36Sopenharmony_ci	if (status == -EIO) {
119062306a36Sopenharmony_ci		/* Read error from buffer only when the FW returned an error */
119162306a36Sopenharmony_ci		struct ice_aqc_download_pkg_resp *resp;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci		resp = (struct ice_aqc_download_pkg_resp *)pkg_buf;
119462306a36Sopenharmony_ci		if (error_offset)
119562306a36Sopenharmony_ci			*error_offset = le32_to_cpu(resp->error_offset);
119662306a36Sopenharmony_ci		if (error_info)
119762306a36Sopenharmony_ci			*error_info = le32_to_cpu(resp->error_info);
119862306a36Sopenharmony_ci	}
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	return status;
120162306a36Sopenharmony_ci}
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci/**
120462306a36Sopenharmony_ci * ice_dwnld_cfg_bufs
120562306a36Sopenharmony_ci * @hw: pointer to the hardware structure
120662306a36Sopenharmony_ci * @bufs: pointer to an array of buffers
120762306a36Sopenharmony_ci * @count: the number of buffers in the array
120862306a36Sopenharmony_ci *
120962306a36Sopenharmony_ci * Obtains global config lock and downloads the package configuration buffers
121062306a36Sopenharmony_ci * to the firmware. Metadata buffers are skipped, and the first metadata buffer
121162306a36Sopenharmony_ci * found indicates that the rest of the buffers are all metadata buffers.
121262306a36Sopenharmony_ci */
121362306a36Sopenharmony_cistatic enum ice_ddp_state ice_dwnld_cfg_bufs(struct ice_hw *hw,
121462306a36Sopenharmony_ci					     struct ice_buf *bufs, u32 count)
121562306a36Sopenharmony_ci{
121662306a36Sopenharmony_ci	enum ice_ddp_state state = ICE_DDP_PKG_SUCCESS;
121762306a36Sopenharmony_ci	struct ice_buf_hdr *bh;
121862306a36Sopenharmony_ci	enum ice_aq_err err;
121962306a36Sopenharmony_ci	u32 offset, info, i;
122062306a36Sopenharmony_ci	int status;
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	if (!bufs || !count)
122362306a36Sopenharmony_ci		return ICE_DDP_PKG_ERR;
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	/* If the first buffer's first section has its metadata bit set
122662306a36Sopenharmony_ci	 * then there are no buffers to be downloaded, and the operation is
122762306a36Sopenharmony_ci	 * considered a success.
122862306a36Sopenharmony_ci	 */
122962306a36Sopenharmony_ci	bh = (struct ice_buf_hdr *)bufs;
123062306a36Sopenharmony_ci	if (le32_to_cpu(bh->section_entry[0].type) & ICE_METADATA_BUF)
123162306a36Sopenharmony_ci		return ICE_DDP_PKG_SUCCESS;
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	status = ice_acquire_global_cfg_lock(hw, ICE_RES_WRITE);
123462306a36Sopenharmony_ci	if (status) {
123562306a36Sopenharmony_ci		if (status == -EALREADY)
123662306a36Sopenharmony_ci			return ICE_DDP_PKG_ALREADY_LOADED;
123762306a36Sopenharmony_ci		return ice_map_aq_err_to_ddp_state(hw->adminq.sq_last_status);
123862306a36Sopenharmony_ci	}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
124162306a36Sopenharmony_ci		bool last = ((i + 1) == count);
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci		if (!last) {
124462306a36Sopenharmony_ci			/* check next buffer for metadata flag */
124562306a36Sopenharmony_ci			bh = (struct ice_buf_hdr *)(bufs + i + 1);
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci			/* A set metadata flag in the next buffer will signal
124862306a36Sopenharmony_ci			 * that the current buffer will be the last buffer
124962306a36Sopenharmony_ci			 * downloaded
125062306a36Sopenharmony_ci			 */
125162306a36Sopenharmony_ci			if (le16_to_cpu(bh->section_count))
125262306a36Sopenharmony_ci				if (le32_to_cpu(bh->section_entry[0].type) &
125362306a36Sopenharmony_ci				    ICE_METADATA_BUF)
125462306a36Sopenharmony_ci					last = true;
125562306a36Sopenharmony_ci		}
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci		bh = (struct ice_buf_hdr *)(bufs + i);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci		status = ice_aq_download_pkg(hw, bh, ICE_PKG_BUF_SIZE, last,
126062306a36Sopenharmony_ci					     &offset, &info, NULL);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci		/* Save AQ status from download package */
126362306a36Sopenharmony_ci		if (status) {
126462306a36Sopenharmony_ci			ice_debug(hw, ICE_DBG_PKG,
126562306a36Sopenharmony_ci				  "Pkg download failed: err %d off %d inf %d\n",
126662306a36Sopenharmony_ci				  status, offset, info);
126762306a36Sopenharmony_ci			err = hw->adminq.sq_last_status;
126862306a36Sopenharmony_ci			state = ice_map_aq_err_to_ddp_state(err);
126962306a36Sopenharmony_ci			break;
127062306a36Sopenharmony_ci		}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci		if (last)
127362306a36Sopenharmony_ci			break;
127462306a36Sopenharmony_ci	}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	if (!status) {
127762306a36Sopenharmony_ci		status = ice_set_vlan_mode(hw);
127862306a36Sopenharmony_ci		if (status)
127962306a36Sopenharmony_ci			ice_debug(hw, ICE_DBG_PKG,
128062306a36Sopenharmony_ci				  "Failed to set VLAN mode: err %d\n", status);
128162306a36Sopenharmony_ci	}
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	ice_release_global_cfg_lock(hw);
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	return state;
128662306a36Sopenharmony_ci}
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci/**
128962306a36Sopenharmony_ci * ice_aq_get_pkg_info_list
129062306a36Sopenharmony_ci * @hw: pointer to the hardware structure
129162306a36Sopenharmony_ci * @pkg_info: the buffer which will receive the information list
129262306a36Sopenharmony_ci * @buf_size: the size of the pkg_info information buffer
129362306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
129462306a36Sopenharmony_ci *
129562306a36Sopenharmony_ci * Get Package Info List (0x0C43)
129662306a36Sopenharmony_ci */
129762306a36Sopenharmony_cistatic int ice_aq_get_pkg_info_list(struct ice_hw *hw,
129862306a36Sopenharmony_ci				    struct ice_aqc_get_pkg_info_resp *pkg_info,
129962306a36Sopenharmony_ci				    u16 buf_size, struct ice_sq_cd *cd)
130062306a36Sopenharmony_ci{
130162306a36Sopenharmony_ci	struct ice_aq_desc desc;
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_pkg_info_list);
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	return ice_aq_send_cmd(hw, &desc, pkg_info, buf_size, cd);
130662306a36Sopenharmony_ci}
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci/**
130962306a36Sopenharmony_ci * ice_download_pkg
131062306a36Sopenharmony_ci * @hw: pointer to the hardware structure
131162306a36Sopenharmony_ci * @ice_seg: pointer to the segment of the package to be downloaded
131262306a36Sopenharmony_ci *
131362306a36Sopenharmony_ci * Handles the download of a complete package.
131462306a36Sopenharmony_ci */
131562306a36Sopenharmony_cistatic enum ice_ddp_state ice_download_pkg(struct ice_hw *hw,
131662306a36Sopenharmony_ci					   struct ice_seg *ice_seg)
131762306a36Sopenharmony_ci{
131862306a36Sopenharmony_ci	struct ice_buf_table *ice_buf_tbl;
131962306a36Sopenharmony_ci	int status;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	ice_debug(hw, ICE_DBG_PKG, "Segment format version: %d.%d.%d.%d\n",
132262306a36Sopenharmony_ci		  ice_seg->hdr.seg_format_ver.major,
132362306a36Sopenharmony_ci		  ice_seg->hdr.seg_format_ver.minor,
132462306a36Sopenharmony_ci		  ice_seg->hdr.seg_format_ver.update,
132562306a36Sopenharmony_ci		  ice_seg->hdr.seg_format_ver.draft);
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	ice_debug(hw, ICE_DBG_PKG, "Seg: type 0x%X, size %d, name %s\n",
132862306a36Sopenharmony_ci		  le32_to_cpu(ice_seg->hdr.seg_type),
132962306a36Sopenharmony_ci		  le32_to_cpu(ice_seg->hdr.seg_size), ice_seg->hdr.seg_id);
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	ice_buf_tbl = ice_find_buf_table(ice_seg);
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	ice_debug(hw, ICE_DBG_PKG, "Seg buf count: %d\n",
133462306a36Sopenharmony_ci		  le32_to_cpu(ice_buf_tbl->buf_count));
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	status = ice_dwnld_cfg_bufs(hw, ice_buf_tbl->buf_array,
133762306a36Sopenharmony_ci				    le32_to_cpu(ice_buf_tbl->buf_count));
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	ice_post_pkg_dwnld_vlan_mode_cfg(hw);
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	return status;
134262306a36Sopenharmony_ci}
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci/**
134562306a36Sopenharmony_ci * ice_aq_update_pkg
134662306a36Sopenharmony_ci * @hw: pointer to the hardware structure
134762306a36Sopenharmony_ci * @pkg_buf: the package cmd buffer
134862306a36Sopenharmony_ci * @buf_size: the size of the package cmd buffer
134962306a36Sopenharmony_ci * @last_buf: last buffer indicator
135062306a36Sopenharmony_ci * @error_offset: returns error offset
135162306a36Sopenharmony_ci * @error_info: returns error information
135262306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
135362306a36Sopenharmony_ci *
135462306a36Sopenharmony_ci * Update Package (0x0C42)
135562306a36Sopenharmony_ci */
135662306a36Sopenharmony_cistatic int ice_aq_update_pkg(struct ice_hw *hw, struct ice_buf_hdr *pkg_buf,
135762306a36Sopenharmony_ci			     u16 buf_size, bool last_buf, u32 *error_offset,
135862306a36Sopenharmony_ci			     u32 *error_info, struct ice_sq_cd *cd)
135962306a36Sopenharmony_ci{
136062306a36Sopenharmony_ci	struct ice_aqc_download_pkg *cmd;
136162306a36Sopenharmony_ci	struct ice_aq_desc desc;
136262306a36Sopenharmony_ci	int status;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	if (error_offset)
136562306a36Sopenharmony_ci		*error_offset = 0;
136662306a36Sopenharmony_ci	if (error_info)
136762306a36Sopenharmony_ci		*error_info = 0;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	cmd = &desc.params.download_pkg;
137062306a36Sopenharmony_ci	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_update_pkg);
137162306a36Sopenharmony_ci	desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	if (last_buf)
137462306a36Sopenharmony_ci		cmd->flags |= ICE_AQC_DOWNLOAD_PKG_LAST_BUF;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	status = ice_aq_send_cmd(hw, &desc, pkg_buf, buf_size, cd);
137762306a36Sopenharmony_ci	if (status == -EIO) {
137862306a36Sopenharmony_ci		/* Read error from buffer only when the FW returned an error */
137962306a36Sopenharmony_ci		struct ice_aqc_download_pkg_resp *resp;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci		resp = (struct ice_aqc_download_pkg_resp *)pkg_buf;
138262306a36Sopenharmony_ci		if (error_offset)
138362306a36Sopenharmony_ci			*error_offset = le32_to_cpu(resp->error_offset);
138462306a36Sopenharmony_ci		if (error_info)
138562306a36Sopenharmony_ci			*error_info = le32_to_cpu(resp->error_info);
138662306a36Sopenharmony_ci	}
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	return status;
138962306a36Sopenharmony_ci}
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci/**
139262306a36Sopenharmony_ci * ice_aq_upload_section
139362306a36Sopenharmony_ci * @hw: pointer to the hardware structure
139462306a36Sopenharmony_ci * @pkg_buf: the package buffer which will receive the section
139562306a36Sopenharmony_ci * @buf_size: the size of the package buffer
139662306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
139762306a36Sopenharmony_ci *
139862306a36Sopenharmony_ci * Upload Section (0x0C41)
139962306a36Sopenharmony_ci */
140062306a36Sopenharmony_ciint ice_aq_upload_section(struct ice_hw *hw, struct ice_buf_hdr *pkg_buf,
140162306a36Sopenharmony_ci			  u16 buf_size, struct ice_sq_cd *cd)
140262306a36Sopenharmony_ci{
140362306a36Sopenharmony_ci	struct ice_aq_desc desc;
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_upload_section);
140662306a36Sopenharmony_ci	desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	return ice_aq_send_cmd(hw, &desc, pkg_buf, buf_size, cd);
140962306a36Sopenharmony_ci}
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci/**
141262306a36Sopenharmony_ci * ice_update_pkg_no_lock
141362306a36Sopenharmony_ci * @hw: pointer to the hardware structure
141462306a36Sopenharmony_ci * @bufs: pointer to an array of buffers
141562306a36Sopenharmony_ci * @count: the number of buffers in the array
141662306a36Sopenharmony_ci */
141762306a36Sopenharmony_ciint ice_update_pkg_no_lock(struct ice_hw *hw, struct ice_buf *bufs, u32 count)
141862306a36Sopenharmony_ci{
141962306a36Sopenharmony_ci	int status = 0;
142062306a36Sopenharmony_ci	u32 i;
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
142362306a36Sopenharmony_ci		struct ice_buf_hdr *bh = (struct ice_buf_hdr *)(bufs + i);
142462306a36Sopenharmony_ci		bool last = ((i + 1) == count);
142562306a36Sopenharmony_ci		u32 offset, info;
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci		status = ice_aq_update_pkg(hw, bh, le16_to_cpu(bh->data_end),
142862306a36Sopenharmony_ci					   last, &offset, &info, NULL);
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci		if (status) {
143162306a36Sopenharmony_ci			ice_debug(hw, ICE_DBG_PKG,
143262306a36Sopenharmony_ci				  "Update pkg failed: err %d off %d inf %d\n",
143362306a36Sopenharmony_ci				  status, offset, info);
143462306a36Sopenharmony_ci			break;
143562306a36Sopenharmony_ci		}
143662306a36Sopenharmony_ci	}
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	return status;
143962306a36Sopenharmony_ci}
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci/**
144262306a36Sopenharmony_ci * ice_update_pkg
144362306a36Sopenharmony_ci * @hw: pointer to the hardware structure
144462306a36Sopenharmony_ci * @bufs: pointer to an array of buffers
144562306a36Sopenharmony_ci * @count: the number of buffers in the array
144662306a36Sopenharmony_ci *
144762306a36Sopenharmony_ci * Obtains change lock and updates package.
144862306a36Sopenharmony_ci */
144962306a36Sopenharmony_ciint ice_update_pkg(struct ice_hw *hw, struct ice_buf *bufs, u32 count)
145062306a36Sopenharmony_ci{
145162306a36Sopenharmony_ci	int status;
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	status = ice_acquire_change_lock(hw, ICE_RES_WRITE);
145462306a36Sopenharmony_ci	if (status)
145562306a36Sopenharmony_ci		return status;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	status = ice_update_pkg_no_lock(hw, bufs, count);
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	ice_release_change_lock(hw);
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	return status;
146262306a36Sopenharmony_ci}
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci/**
146562306a36Sopenharmony_ci * ice_find_seg_in_pkg
146662306a36Sopenharmony_ci * @hw: pointer to the hardware structure
146762306a36Sopenharmony_ci * @seg_type: the segment type to search for (i.e., SEGMENT_TYPE_CPK)
146862306a36Sopenharmony_ci * @pkg_hdr: pointer to the package header to be searched
146962306a36Sopenharmony_ci *
147062306a36Sopenharmony_ci * This function searches a package file for a particular segment type. On
147162306a36Sopenharmony_ci * success it returns a pointer to the segment header, otherwise it will
147262306a36Sopenharmony_ci * return NULL.
147362306a36Sopenharmony_ci */
147462306a36Sopenharmony_cistatic struct ice_generic_seg_hdr *
147562306a36Sopenharmony_ciice_find_seg_in_pkg(struct ice_hw *hw, u32 seg_type,
147662306a36Sopenharmony_ci		    struct ice_pkg_hdr *pkg_hdr)
147762306a36Sopenharmony_ci{
147862306a36Sopenharmony_ci	u32 i;
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	ice_debug(hw, ICE_DBG_PKG, "Package format version: %d.%d.%d.%d\n",
148162306a36Sopenharmony_ci		  pkg_hdr->pkg_format_ver.major, pkg_hdr->pkg_format_ver.minor,
148262306a36Sopenharmony_ci		  pkg_hdr->pkg_format_ver.update,
148362306a36Sopenharmony_ci		  pkg_hdr->pkg_format_ver.draft);
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	/* Search all package segments for the requested segment type */
148662306a36Sopenharmony_ci	for (i = 0; i < le32_to_cpu(pkg_hdr->seg_count); i++) {
148762306a36Sopenharmony_ci		struct ice_generic_seg_hdr *seg;
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci		seg = (struct ice_generic_seg_hdr
149062306a36Sopenharmony_ci			       *)((u8 *)pkg_hdr +
149162306a36Sopenharmony_ci				  le32_to_cpu(pkg_hdr->seg_offset[i]));
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci		if (le32_to_cpu(seg->seg_type) == seg_type)
149462306a36Sopenharmony_ci			return seg;
149562306a36Sopenharmony_ci	}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	return NULL;
149862306a36Sopenharmony_ci}
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci/**
150162306a36Sopenharmony_ci * ice_init_pkg_info
150262306a36Sopenharmony_ci * @hw: pointer to the hardware structure
150362306a36Sopenharmony_ci * @pkg_hdr: pointer to the driver's package hdr
150462306a36Sopenharmony_ci *
150562306a36Sopenharmony_ci * Saves off the package details into the HW structure.
150662306a36Sopenharmony_ci */
150762306a36Sopenharmony_cistatic enum ice_ddp_state ice_init_pkg_info(struct ice_hw *hw,
150862306a36Sopenharmony_ci					    struct ice_pkg_hdr *pkg_hdr)
150962306a36Sopenharmony_ci{
151062306a36Sopenharmony_ci	struct ice_generic_seg_hdr *seg_hdr;
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	if (!pkg_hdr)
151362306a36Sopenharmony_ci		return ICE_DDP_PKG_ERR;
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	seg_hdr = ice_find_seg_in_pkg(hw, SEGMENT_TYPE_ICE, pkg_hdr);
151662306a36Sopenharmony_ci	if (seg_hdr) {
151762306a36Sopenharmony_ci		struct ice_meta_sect *meta;
151862306a36Sopenharmony_ci		struct ice_pkg_enum state;
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci		memset(&state, 0, sizeof(state));
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci		/* Get package information from the Metadata Section */
152362306a36Sopenharmony_ci		meta = ice_pkg_enum_section((struct ice_seg *)seg_hdr, &state,
152462306a36Sopenharmony_ci					    ICE_SID_METADATA);
152562306a36Sopenharmony_ci		if (!meta) {
152662306a36Sopenharmony_ci			ice_debug(hw, ICE_DBG_INIT,
152762306a36Sopenharmony_ci				  "Did not find ice metadata section in package\n");
152862306a36Sopenharmony_ci			return ICE_DDP_PKG_INVALID_FILE;
152962306a36Sopenharmony_ci		}
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci		hw->pkg_ver = meta->ver;
153262306a36Sopenharmony_ci		memcpy(hw->pkg_name, meta->name, sizeof(meta->name));
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_PKG, "Pkg: %d.%d.%d.%d, %s\n",
153562306a36Sopenharmony_ci			  meta->ver.major, meta->ver.minor, meta->ver.update,
153662306a36Sopenharmony_ci			  meta->ver.draft, meta->name);
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci		hw->ice_seg_fmt_ver = seg_hdr->seg_format_ver;
153962306a36Sopenharmony_ci		memcpy(hw->ice_seg_id, seg_hdr->seg_id, sizeof(hw->ice_seg_id));
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_PKG, "Ice Seg: %d.%d.%d.%d, %s\n",
154262306a36Sopenharmony_ci			  seg_hdr->seg_format_ver.major,
154362306a36Sopenharmony_ci			  seg_hdr->seg_format_ver.minor,
154462306a36Sopenharmony_ci			  seg_hdr->seg_format_ver.update,
154562306a36Sopenharmony_ci			  seg_hdr->seg_format_ver.draft, seg_hdr->seg_id);
154662306a36Sopenharmony_ci	} else {
154762306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_INIT,
154862306a36Sopenharmony_ci			  "Did not find ice segment in driver package\n");
154962306a36Sopenharmony_ci		return ICE_DDP_PKG_INVALID_FILE;
155062306a36Sopenharmony_ci	}
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	return ICE_DDP_PKG_SUCCESS;
155362306a36Sopenharmony_ci}
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci/**
155662306a36Sopenharmony_ci * ice_get_pkg_info
155762306a36Sopenharmony_ci * @hw: pointer to the hardware structure
155862306a36Sopenharmony_ci *
155962306a36Sopenharmony_ci * Store details of the package currently loaded in HW into the HW structure.
156062306a36Sopenharmony_ci */
156162306a36Sopenharmony_cistatic enum ice_ddp_state ice_get_pkg_info(struct ice_hw *hw)
156262306a36Sopenharmony_ci{
156362306a36Sopenharmony_ci	enum ice_ddp_state state = ICE_DDP_PKG_SUCCESS;
156462306a36Sopenharmony_ci	struct ice_aqc_get_pkg_info_resp *pkg_info;
156562306a36Sopenharmony_ci	u16 size;
156662306a36Sopenharmony_ci	u32 i;
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	size = struct_size(pkg_info, pkg_info, ICE_PKG_CNT);
156962306a36Sopenharmony_ci	pkg_info = kzalloc(size, GFP_KERNEL);
157062306a36Sopenharmony_ci	if (!pkg_info)
157162306a36Sopenharmony_ci		return ICE_DDP_PKG_ERR;
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	if (ice_aq_get_pkg_info_list(hw, pkg_info, size, NULL)) {
157462306a36Sopenharmony_ci		state = ICE_DDP_PKG_ERR;
157562306a36Sopenharmony_ci		goto init_pkg_free_alloc;
157662306a36Sopenharmony_ci	}
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	for (i = 0; i < le32_to_cpu(pkg_info->count); i++) {
157962306a36Sopenharmony_ci#define ICE_PKG_FLAG_COUNT 4
158062306a36Sopenharmony_ci		char flags[ICE_PKG_FLAG_COUNT + 1] = { 0 };
158162306a36Sopenharmony_ci		u8 place = 0;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci		if (pkg_info->pkg_info[i].is_active) {
158462306a36Sopenharmony_ci			flags[place++] = 'A';
158562306a36Sopenharmony_ci			hw->active_pkg_ver = pkg_info->pkg_info[i].ver;
158662306a36Sopenharmony_ci			hw->active_track_id =
158762306a36Sopenharmony_ci				le32_to_cpu(pkg_info->pkg_info[i].track_id);
158862306a36Sopenharmony_ci			memcpy(hw->active_pkg_name, pkg_info->pkg_info[i].name,
158962306a36Sopenharmony_ci			       sizeof(pkg_info->pkg_info[i].name));
159062306a36Sopenharmony_ci			hw->active_pkg_in_nvm = pkg_info->pkg_info[i].is_in_nvm;
159162306a36Sopenharmony_ci		}
159262306a36Sopenharmony_ci		if (pkg_info->pkg_info[i].is_active_at_boot)
159362306a36Sopenharmony_ci			flags[place++] = 'B';
159462306a36Sopenharmony_ci		if (pkg_info->pkg_info[i].is_modified)
159562306a36Sopenharmony_ci			flags[place++] = 'M';
159662306a36Sopenharmony_ci		if (pkg_info->pkg_info[i].is_in_nvm)
159762306a36Sopenharmony_ci			flags[place++] = 'N';
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_PKG, "Pkg[%d]: %d.%d.%d.%d,%s,%s\n", i,
160062306a36Sopenharmony_ci			  pkg_info->pkg_info[i].ver.major,
160162306a36Sopenharmony_ci			  pkg_info->pkg_info[i].ver.minor,
160262306a36Sopenharmony_ci			  pkg_info->pkg_info[i].ver.update,
160362306a36Sopenharmony_ci			  pkg_info->pkg_info[i].ver.draft,
160462306a36Sopenharmony_ci			  pkg_info->pkg_info[i].name, flags);
160562306a36Sopenharmony_ci	}
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ciinit_pkg_free_alloc:
160862306a36Sopenharmony_ci	kfree(pkg_info);
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	return state;
161162306a36Sopenharmony_ci}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci/**
161462306a36Sopenharmony_ci * ice_chk_pkg_compat
161562306a36Sopenharmony_ci * @hw: pointer to the hardware structure
161662306a36Sopenharmony_ci * @ospkg: pointer to the package hdr
161762306a36Sopenharmony_ci * @seg: pointer to the package segment hdr
161862306a36Sopenharmony_ci *
161962306a36Sopenharmony_ci * This function checks the package version compatibility with driver and NVM
162062306a36Sopenharmony_ci */
162162306a36Sopenharmony_cistatic enum ice_ddp_state ice_chk_pkg_compat(struct ice_hw *hw,
162262306a36Sopenharmony_ci					     struct ice_pkg_hdr *ospkg,
162362306a36Sopenharmony_ci					     struct ice_seg **seg)
162462306a36Sopenharmony_ci{
162562306a36Sopenharmony_ci	struct ice_aqc_get_pkg_info_resp *pkg;
162662306a36Sopenharmony_ci	enum ice_ddp_state state;
162762306a36Sopenharmony_ci	u16 size;
162862306a36Sopenharmony_ci	u32 i;
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	/* Check package version compatibility */
163162306a36Sopenharmony_ci	state = ice_chk_pkg_version(&hw->pkg_ver);
163262306a36Sopenharmony_ci	if (state) {
163362306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_INIT, "Package version check failed.\n");
163462306a36Sopenharmony_ci		return state;
163562306a36Sopenharmony_ci	}
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	/* find ICE segment in given package */
163862306a36Sopenharmony_ci	*seg = (struct ice_seg *)ice_find_seg_in_pkg(hw, SEGMENT_TYPE_ICE,
163962306a36Sopenharmony_ci						     ospkg);
164062306a36Sopenharmony_ci	if (!*seg) {
164162306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_INIT, "no ice segment in package.\n");
164262306a36Sopenharmony_ci		return ICE_DDP_PKG_INVALID_FILE;
164362306a36Sopenharmony_ci	}
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	/* Check if FW is compatible with the OS package */
164662306a36Sopenharmony_ci	size = struct_size(pkg, pkg_info, ICE_PKG_CNT);
164762306a36Sopenharmony_ci	pkg = kzalloc(size, GFP_KERNEL);
164862306a36Sopenharmony_ci	if (!pkg)
164962306a36Sopenharmony_ci		return ICE_DDP_PKG_ERR;
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	if (ice_aq_get_pkg_info_list(hw, pkg, size, NULL)) {
165262306a36Sopenharmony_ci		state = ICE_DDP_PKG_LOAD_ERROR;
165362306a36Sopenharmony_ci		goto fw_ddp_compat_free_alloc;
165462306a36Sopenharmony_ci	}
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	for (i = 0; i < le32_to_cpu(pkg->count); i++) {
165762306a36Sopenharmony_ci		/* loop till we find the NVM package */
165862306a36Sopenharmony_ci		if (!pkg->pkg_info[i].is_in_nvm)
165962306a36Sopenharmony_ci			continue;
166062306a36Sopenharmony_ci		if ((*seg)->hdr.seg_format_ver.major !=
166162306a36Sopenharmony_ci			    pkg->pkg_info[i].ver.major ||
166262306a36Sopenharmony_ci		    (*seg)->hdr.seg_format_ver.minor >
166362306a36Sopenharmony_ci			    pkg->pkg_info[i].ver.minor) {
166462306a36Sopenharmony_ci			state = ICE_DDP_PKG_FW_MISMATCH;
166562306a36Sopenharmony_ci			ice_debug(hw, ICE_DBG_INIT,
166662306a36Sopenharmony_ci				  "OS package is not compatible with NVM.\n");
166762306a36Sopenharmony_ci		}
166862306a36Sopenharmony_ci		/* done processing NVM package so break */
166962306a36Sopenharmony_ci		break;
167062306a36Sopenharmony_ci	}
167162306a36Sopenharmony_cifw_ddp_compat_free_alloc:
167262306a36Sopenharmony_ci	kfree(pkg);
167362306a36Sopenharmony_ci	return state;
167462306a36Sopenharmony_ci}
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci/**
167762306a36Sopenharmony_ci * ice_init_pkg_hints
167862306a36Sopenharmony_ci * @hw: pointer to the HW structure
167962306a36Sopenharmony_ci * @ice_seg: pointer to the segment of the package scan (non-NULL)
168062306a36Sopenharmony_ci *
168162306a36Sopenharmony_ci * This function will scan the package and save off relevant information
168262306a36Sopenharmony_ci * (hints or metadata) for driver use. The ice_seg parameter must not be NULL
168362306a36Sopenharmony_ci * since the first call to ice_enum_labels requires a pointer to an actual
168462306a36Sopenharmony_ci * ice_seg structure.
168562306a36Sopenharmony_ci */
168662306a36Sopenharmony_cistatic void ice_init_pkg_hints(struct ice_hw *hw, struct ice_seg *ice_seg)
168762306a36Sopenharmony_ci{
168862306a36Sopenharmony_ci	struct ice_pkg_enum state;
168962306a36Sopenharmony_ci	char *label_name;
169062306a36Sopenharmony_ci	u16 val;
169162306a36Sopenharmony_ci	int i;
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	memset(&hw->tnl, 0, sizeof(hw->tnl));
169462306a36Sopenharmony_ci	memset(&state, 0, sizeof(state));
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci	if (!ice_seg)
169762306a36Sopenharmony_ci		return;
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	label_name = ice_enum_labels(ice_seg, ICE_SID_LBL_RXPARSER_TMEM, &state,
170062306a36Sopenharmony_ci				     &val);
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	while (label_name) {
170362306a36Sopenharmony_ci		if (!strncmp(label_name, ICE_TNL_PRE, strlen(ICE_TNL_PRE)))
170462306a36Sopenharmony_ci			/* check for a tunnel entry */
170562306a36Sopenharmony_ci			ice_add_tunnel_hint(hw, label_name, val);
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci		/* check for a dvm mode entry */
170862306a36Sopenharmony_ci		else if (!strncmp(label_name, ICE_DVM_PRE, strlen(ICE_DVM_PRE)))
170962306a36Sopenharmony_ci			ice_add_dvm_hint(hw, val, true);
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci		/* check for a svm mode entry */
171262306a36Sopenharmony_ci		else if (!strncmp(label_name, ICE_SVM_PRE, strlen(ICE_SVM_PRE)))
171362306a36Sopenharmony_ci			ice_add_dvm_hint(hw, val, false);
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci		label_name = ice_enum_labels(NULL, 0, &state, &val);
171662306a36Sopenharmony_ci	}
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	/* Cache the appropriate boost TCAM entry pointers for tunnels */
171962306a36Sopenharmony_ci	for (i = 0; i < hw->tnl.count; i++) {
172062306a36Sopenharmony_ci		ice_find_boost_entry(ice_seg, hw->tnl.tbl[i].boost_addr,
172162306a36Sopenharmony_ci				     &hw->tnl.tbl[i].boost_entry);
172262306a36Sopenharmony_ci		if (hw->tnl.tbl[i].boost_entry) {
172362306a36Sopenharmony_ci			hw->tnl.tbl[i].valid = true;
172462306a36Sopenharmony_ci			if (hw->tnl.tbl[i].type < __TNL_TYPE_CNT)
172562306a36Sopenharmony_ci				hw->tnl.valid_count[hw->tnl.tbl[i].type]++;
172662306a36Sopenharmony_ci		}
172762306a36Sopenharmony_ci	}
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	/* Cache the appropriate boost TCAM entry pointers for DVM and SVM */
173062306a36Sopenharmony_ci	for (i = 0; i < hw->dvm_upd.count; i++)
173162306a36Sopenharmony_ci		ice_find_boost_entry(ice_seg, hw->dvm_upd.tbl[i].boost_addr,
173262306a36Sopenharmony_ci				     &hw->dvm_upd.tbl[i].boost_entry);
173362306a36Sopenharmony_ci}
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci/**
173662306a36Sopenharmony_ci * ice_fill_hw_ptype - fill the enabled PTYPE bit information
173762306a36Sopenharmony_ci * @hw: pointer to the HW structure
173862306a36Sopenharmony_ci */
173962306a36Sopenharmony_cistatic void ice_fill_hw_ptype(struct ice_hw *hw)
174062306a36Sopenharmony_ci{
174162306a36Sopenharmony_ci	struct ice_marker_ptype_tcam_entry *tcam;
174262306a36Sopenharmony_ci	struct ice_seg *seg = hw->seg;
174362306a36Sopenharmony_ci	struct ice_pkg_enum state;
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	bitmap_zero(hw->hw_ptype, ICE_FLOW_PTYPE_MAX);
174662306a36Sopenharmony_ci	if (!seg)
174762306a36Sopenharmony_ci		return;
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	memset(&state, 0, sizeof(state));
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	do {
175262306a36Sopenharmony_ci		tcam = ice_pkg_enum_entry(seg, &state,
175362306a36Sopenharmony_ci					  ICE_SID_RXPARSER_MARKER_PTYPE, NULL,
175462306a36Sopenharmony_ci					  ice_marker_ptype_tcam_handler);
175562306a36Sopenharmony_ci		if (tcam &&
175662306a36Sopenharmony_ci		    le16_to_cpu(tcam->addr) < ICE_MARKER_PTYPE_TCAM_ADDR_MAX &&
175762306a36Sopenharmony_ci		    le16_to_cpu(tcam->ptype) < ICE_FLOW_PTYPE_MAX)
175862306a36Sopenharmony_ci			set_bit(le16_to_cpu(tcam->ptype), hw->hw_ptype);
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci		seg = NULL;
176162306a36Sopenharmony_ci	} while (tcam);
176262306a36Sopenharmony_ci}
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci/**
176562306a36Sopenharmony_ci * ice_init_pkg - initialize/download package
176662306a36Sopenharmony_ci * @hw: pointer to the hardware structure
176762306a36Sopenharmony_ci * @buf: pointer to the package buffer
176862306a36Sopenharmony_ci * @len: size of the package buffer
176962306a36Sopenharmony_ci *
177062306a36Sopenharmony_ci * This function initializes a package. The package contains HW tables
177162306a36Sopenharmony_ci * required to do packet processing. First, the function extracts package
177262306a36Sopenharmony_ci * information such as version. Then it finds the ice configuration segment
177362306a36Sopenharmony_ci * within the package; this function then saves a copy of the segment pointer
177462306a36Sopenharmony_ci * within the supplied package buffer. Next, the function will cache any hints
177562306a36Sopenharmony_ci * from the package, followed by downloading the package itself. Note, that if
177662306a36Sopenharmony_ci * a previous PF driver has already downloaded the package successfully, then
177762306a36Sopenharmony_ci * the current driver will not have to download the package again.
177862306a36Sopenharmony_ci *
177962306a36Sopenharmony_ci * The local package contents will be used to query default behavior and to
178062306a36Sopenharmony_ci * update specific sections of the HW's version of the package (e.g. to update
178162306a36Sopenharmony_ci * the parse graph to understand new protocols).
178262306a36Sopenharmony_ci *
178362306a36Sopenharmony_ci * This function stores a pointer to the package buffer memory, and it is
178462306a36Sopenharmony_ci * expected that the supplied buffer will not be freed immediately. If the
178562306a36Sopenharmony_ci * package buffer needs to be freed, such as when read from a file, use
178662306a36Sopenharmony_ci * ice_copy_and_init_pkg() instead of directly calling ice_init_pkg() in this
178762306a36Sopenharmony_ci * case.
178862306a36Sopenharmony_ci */
178962306a36Sopenharmony_cienum ice_ddp_state ice_init_pkg(struct ice_hw *hw, u8 *buf, u32 len)
179062306a36Sopenharmony_ci{
179162306a36Sopenharmony_ci	bool already_loaded = false;
179262306a36Sopenharmony_ci	enum ice_ddp_state state;
179362306a36Sopenharmony_ci	struct ice_pkg_hdr *pkg;
179462306a36Sopenharmony_ci	struct ice_seg *seg;
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	if (!buf || !len)
179762306a36Sopenharmony_ci		return ICE_DDP_PKG_ERR;
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci	pkg = (struct ice_pkg_hdr *)buf;
180062306a36Sopenharmony_ci	state = ice_verify_pkg(pkg, len);
180162306a36Sopenharmony_ci	if (state) {
180262306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_INIT, "failed to verify pkg (err: %d)\n",
180362306a36Sopenharmony_ci			  state);
180462306a36Sopenharmony_ci		return state;
180562306a36Sopenharmony_ci	}
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci	/* initialize package info */
180862306a36Sopenharmony_ci	state = ice_init_pkg_info(hw, pkg);
180962306a36Sopenharmony_ci	if (state)
181062306a36Sopenharmony_ci		return state;
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci	/* before downloading the package, check package version for
181362306a36Sopenharmony_ci	 * compatibility with driver
181462306a36Sopenharmony_ci	 */
181562306a36Sopenharmony_ci	state = ice_chk_pkg_compat(hw, pkg, &seg);
181662306a36Sopenharmony_ci	if (state)
181762306a36Sopenharmony_ci		return state;
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	/* initialize package hints and then download package */
182062306a36Sopenharmony_ci	ice_init_pkg_hints(hw, seg);
182162306a36Sopenharmony_ci	state = ice_download_pkg(hw, seg);
182262306a36Sopenharmony_ci	if (state == ICE_DDP_PKG_ALREADY_LOADED) {
182362306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_INIT,
182462306a36Sopenharmony_ci			  "package previously loaded - no work.\n");
182562306a36Sopenharmony_ci		already_loaded = true;
182662306a36Sopenharmony_ci	}
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci	/* Get information on the package currently loaded in HW, then make sure
182962306a36Sopenharmony_ci	 * the driver is compatible with this version.
183062306a36Sopenharmony_ci	 */
183162306a36Sopenharmony_ci	if (!state || state == ICE_DDP_PKG_ALREADY_LOADED) {
183262306a36Sopenharmony_ci		state = ice_get_pkg_info(hw);
183362306a36Sopenharmony_ci		if (!state)
183462306a36Sopenharmony_ci			state = ice_get_ddp_pkg_state(hw, already_loaded);
183562306a36Sopenharmony_ci	}
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	if (ice_is_init_pkg_successful(state)) {
183862306a36Sopenharmony_ci		hw->seg = seg;
183962306a36Sopenharmony_ci		/* on successful package download update other required
184062306a36Sopenharmony_ci		 * registers to support the package and fill HW tables
184162306a36Sopenharmony_ci		 * with package content.
184262306a36Sopenharmony_ci		 */
184362306a36Sopenharmony_ci		ice_init_pkg_regs(hw);
184462306a36Sopenharmony_ci		ice_fill_blk_tbls(hw);
184562306a36Sopenharmony_ci		ice_fill_hw_ptype(hw);
184662306a36Sopenharmony_ci		ice_get_prof_index_max(hw);
184762306a36Sopenharmony_ci	} else {
184862306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_INIT, "package load failed, %d\n", state);
184962306a36Sopenharmony_ci	}
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	return state;
185262306a36Sopenharmony_ci}
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci/**
185562306a36Sopenharmony_ci * ice_copy_and_init_pkg - initialize/download a copy of the package
185662306a36Sopenharmony_ci * @hw: pointer to the hardware structure
185762306a36Sopenharmony_ci * @buf: pointer to the package buffer
185862306a36Sopenharmony_ci * @len: size of the package buffer
185962306a36Sopenharmony_ci *
186062306a36Sopenharmony_ci * This function copies the package buffer, and then calls ice_init_pkg() to
186162306a36Sopenharmony_ci * initialize the copied package contents.
186262306a36Sopenharmony_ci *
186362306a36Sopenharmony_ci * The copying is necessary if the package buffer supplied is constant, or if
186462306a36Sopenharmony_ci * the memory may disappear shortly after calling this function.
186562306a36Sopenharmony_ci *
186662306a36Sopenharmony_ci * If the package buffer resides in the data segment and can be modified, the
186762306a36Sopenharmony_ci * caller is free to use ice_init_pkg() instead of ice_copy_and_init_pkg().
186862306a36Sopenharmony_ci *
186962306a36Sopenharmony_ci * However, if the package buffer needs to be copied first, such as when being
187062306a36Sopenharmony_ci * read from a file, the caller should use ice_copy_and_init_pkg().
187162306a36Sopenharmony_ci *
187262306a36Sopenharmony_ci * This function will first copy the package buffer, before calling
187362306a36Sopenharmony_ci * ice_init_pkg(). The caller is free to immediately destroy the original
187462306a36Sopenharmony_ci * package buffer, as the new copy will be managed by this function and
187562306a36Sopenharmony_ci * related routines.
187662306a36Sopenharmony_ci */
187762306a36Sopenharmony_cienum ice_ddp_state ice_copy_and_init_pkg(struct ice_hw *hw, const u8 *buf,
187862306a36Sopenharmony_ci					 u32 len)
187962306a36Sopenharmony_ci{
188062306a36Sopenharmony_ci	enum ice_ddp_state state;
188162306a36Sopenharmony_ci	u8 *buf_copy;
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	if (!buf || !len)
188462306a36Sopenharmony_ci		return ICE_DDP_PKG_ERR;
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	buf_copy = devm_kmemdup(ice_hw_to_dev(hw), buf, len, GFP_KERNEL);
188762306a36Sopenharmony_ci
188862306a36Sopenharmony_ci	state = ice_init_pkg(hw, buf_copy, len);
188962306a36Sopenharmony_ci	if (!ice_is_init_pkg_successful(state)) {
189062306a36Sopenharmony_ci		/* Free the copy, since we failed to initialize the package */
189162306a36Sopenharmony_ci		devm_kfree(ice_hw_to_dev(hw), buf_copy);
189262306a36Sopenharmony_ci	} else {
189362306a36Sopenharmony_ci		/* Track the copied pkg so we can free it later */
189462306a36Sopenharmony_ci		hw->pkg_copy = buf_copy;
189562306a36Sopenharmony_ci		hw->pkg_size = len;
189662306a36Sopenharmony_ci	}
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci	return state;
189962306a36Sopenharmony_ci}
1900