162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2018, Intel Corporation. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/vmalloc.h>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include "ice_common.h"
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/**
962306a36Sopenharmony_ci * ice_aq_read_nvm
1062306a36Sopenharmony_ci * @hw: pointer to the HW struct
1162306a36Sopenharmony_ci * @module_typeid: module pointer location in words from the NVM beginning
1262306a36Sopenharmony_ci * @offset: byte offset from the module beginning
1362306a36Sopenharmony_ci * @length: length of the section to be read (in bytes from the offset)
1462306a36Sopenharmony_ci * @data: command buffer (size [bytes] = length)
1562306a36Sopenharmony_ci * @last_command: tells if this is the last command in a series
1662306a36Sopenharmony_ci * @read_shadow_ram: tell if this is a shadow RAM read
1762306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * Read the NVM using the admin queue commands (0x0701)
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_cistatic int
2262306a36Sopenharmony_ciice_aq_read_nvm(struct ice_hw *hw, u16 module_typeid, u32 offset, u16 length,
2362306a36Sopenharmony_ci		void *data, bool last_command, bool read_shadow_ram,
2462306a36Sopenharmony_ci		struct ice_sq_cd *cd)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	struct ice_aq_desc desc;
2762306a36Sopenharmony_ci	struct ice_aqc_nvm *cmd;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	cmd = &desc.params.nvm;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	if (offset > ICE_AQC_NVM_MAX_OFFSET)
3262306a36Sopenharmony_ci		return -EINVAL;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_nvm_read);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	if (!read_shadow_ram && module_typeid == ICE_AQC_NVM_START_POINT)
3762306a36Sopenharmony_ci		cmd->cmd_flags |= ICE_AQC_NVM_FLASH_ONLY;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	/* If this is the last command in a series, set the proper flag. */
4062306a36Sopenharmony_ci	if (last_command)
4162306a36Sopenharmony_ci		cmd->cmd_flags |= ICE_AQC_NVM_LAST_CMD;
4262306a36Sopenharmony_ci	cmd->module_typeid = cpu_to_le16(module_typeid);
4362306a36Sopenharmony_ci	cmd->offset_low = cpu_to_le16(offset & 0xFFFF);
4462306a36Sopenharmony_ci	cmd->offset_high = (offset >> 16) & 0xFF;
4562306a36Sopenharmony_ci	cmd->length = cpu_to_le16(length);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return ice_aq_send_cmd(hw, &desc, data, length, cd);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/**
5162306a36Sopenharmony_ci * ice_read_flat_nvm - Read portion of NVM by flat offset
5262306a36Sopenharmony_ci * @hw: pointer to the HW struct
5362306a36Sopenharmony_ci * @offset: offset from beginning of NVM
5462306a36Sopenharmony_ci * @length: (in) number of bytes to read; (out) number of bytes actually read
5562306a36Sopenharmony_ci * @data: buffer to return data in (sized to fit the specified length)
5662306a36Sopenharmony_ci * @read_shadow_ram: if true, read from shadow RAM instead of NVM
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci * Reads a portion of the NVM, as a flat memory space. This function correctly
5962306a36Sopenharmony_ci * breaks read requests across Shadow RAM sectors and ensures that no single
6062306a36Sopenharmony_ci * read request exceeds the maximum 4KB read for a single AdminQ command.
6162306a36Sopenharmony_ci *
6262306a36Sopenharmony_ci * Returns a status code on failure. Note that the data pointer may be
6362306a36Sopenharmony_ci * partially updated if some reads succeed before a failure.
6462306a36Sopenharmony_ci */
6562306a36Sopenharmony_ciint
6662306a36Sopenharmony_ciice_read_flat_nvm(struct ice_hw *hw, u32 offset, u32 *length, u8 *data,
6762306a36Sopenharmony_ci		  bool read_shadow_ram)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	u32 inlen = *length;
7062306a36Sopenharmony_ci	u32 bytes_read = 0;
7162306a36Sopenharmony_ci	bool last_cmd;
7262306a36Sopenharmony_ci	int status;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	*length = 0;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/* Verify the length of the read if this is for the Shadow RAM */
7762306a36Sopenharmony_ci	if (read_shadow_ram && ((offset + inlen) > (hw->flash.sr_words * 2u))) {
7862306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "NVM error: requested offset is beyond Shadow RAM limit\n");
7962306a36Sopenharmony_ci		return -EINVAL;
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	do {
8362306a36Sopenharmony_ci		u32 read_size, sector_offset;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci		/* ice_aq_read_nvm cannot read more than 4KB at a time.
8662306a36Sopenharmony_ci		 * Additionally, a read from the Shadow RAM may not cross over
8762306a36Sopenharmony_ci		 * a sector boundary. Conveniently, the sector size is also
8862306a36Sopenharmony_ci		 * 4KB.
8962306a36Sopenharmony_ci		 */
9062306a36Sopenharmony_ci		sector_offset = offset % ICE_AQ_MAX_BUF_LEN;
9162306a36Sopenharmony_ci		read_size = min_t(u32, ICE_AQ_MAX_BUF_LEN - sector_offset,
9262306a36Sopenharmony_ci				  inlen - bytes_read);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		last_cmd = !(bytes_read + read_size < inlen);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci		status = ice_aq_read_nvm(hw, ICE_AQC_NVM_START_POINT,
9762306a36Sopenharmony_ci					 offset, read_size,
9862306a36Sopenharmony_ci					 data + bytes_read, last_cmd,
9962306a36Sopenharmony_ci					 read_shadow_ram, NULL);
10062306a36Sopenharmony_ci		if (status)
10162306a36Sopenharmony_ci			break;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		bytes_read += read_size;
10462306a36Sopenharmony_ci		offset += read_size;
10562306a36Sopenharmony_ci	} while (!last_cmd);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	*length = bytes_read;
10862306a36Sopenharmony_ci	return status;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/**
11262306a36Sopenharmony_ci * ice_aq_update_nvm
11362306a36Sopenharmony_ci * @hw: pointer to the HW struct
11462306a36Sopenharmony_ci * @module_typeid: module pointer location in words from the NVM beginning
11562306a36Sopenharmony_ci * @offset: byte offset from the module beginning
11662306a36Sopenharmony_ci * @length: length of the section to be written (in bytes from the offset)
11762306a36Sopenharmony_ci * @data: command buffer (size [bytes] = length)
11862306a36Sopenharmony_ci * @last_command: tells if this is the last command in a series
11962306a36Sopenharmony_ci * @command_flags: command parameters
12062306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
12162306a36Sopenharmony_ci *
12262306a36Sopenharmony_ci * Update the NVM using the admin queue commands (0x0703)
12362306a36Sopenharmony_ci */
12462306a36Sopenharmony_ciint
12562306a36Sopenharmony_ciice_aq_update_nvm(struct ice_hw *hw, u16 module_typeid, u32 offset,
12662306a36Sopenharmony_ci		  u16 length, void *data, bool last_command, u8 command_flags,
12762306a36Sopenharmony_ci		  struct ice_sq_cd *cd)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct ice_aq_desc desc;
13062306a36Sopenharmony_ci	struct ice_aqc_nvm *cmd;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	cmd = &desc.params.nvm;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/* In offset the highest byte must be zeroed. */
13562306a36Sopenharmony_ci	if (offset & 0xFF000000)
13662306a36Sopenharmony_ci		return -EINVAL;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_nvm_write);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	cmd->cmd_flags |= command_flags;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* If this is the last command in a series, set the proper flag. */
14362306a36Sopenharmony_ci	if (last_command)
14462306a36Sopenharmony_ci		cmd->cmd_flags |= ICE_AQC_NVM_LAST_CMD;
14562306a36Sopenharmony_ci	cmd->module_typeid = cpu_to_le16(module_typeid);
14662306a36Sopenharmony_ci	cmd->offset_low = cpu_to_le16(offset & 0xFFFF);
14762306a36Sopenharmony_ci	cmd->offset_high = (offset >> 16) & 0xFF;
14862306a36Sopenharmony_ci	cmd->length = cpu_to_le16(length);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	return ice_aq_send_cmd(hw, &desc, data, length, cd);
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/**
15662306a36Sopenharmony_ci * ice_aq_erase_nvm
15762306a36Sopenharmony_ci * @hw: pointer to the HW struct
15862306a36Sopenharmony_ci * @module_typeid: module pointer location in words from the NVM beginning
15962306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
16062306a36Sopenharmony_ci *
16162306a36Sopenharmony_ci * Erase the NVM sector using the admin queue commands (0x0702)
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_ciint ice_aq_erase_nvm(struct ice_hw *hw, u16 module_typeid, struct ice_sq_cd *cd)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	struct ice_aq_desc desc;
16662306a36Sopenharmony_ci	struct ice_aqc_nvm *cmd;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	cmd = &desc.params.nvm;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_nvm_erase);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	cmd->module_typeid = cpu_to_le16(module_typeid);
17362306a36Sopenharmony_ci	cmd->length = cpu_to_le16(ICE_AQC_NVM_ERASE_LEN);
17462306a36Sopenharmony_ci	cmd->offset_low = 0;
17562306a36Sopenharmony_ci	cmd->offset_high = 0;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci/**
18162306a36Sopenharmony_ci * ice_read_sr_word_aq - Reads Shadow RAM via AQ
18262306a36Sopenharmony_ci * @hw: pointer to the HW structure
18362306a36Sopenharmony_ci * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF)
18462306a36Sopenharmony_ci * @data: word read from the Shadow RAM
18562306a36Sopenharmony_ci *
18662306a36Sopenharmony_ci * Reads one 16 bit word from the Shadow RAM using ice_read_flat_nvm.
18762306a36Sopenharmony_ci */
18862306a36Sopenharmony_cistatic int ice_read_sr_word_aq(struct ice_hw *hw, u16 offset, u16 *data)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	u32 bytes = sizeof(u16);
19162306a36Sopenharmony_ci	__le16 data_local;
19262306a36Sopenharmony_ci	int status;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/* Note that ice_read_flat_nvm takes into account the 4Kb AdminQ and
19562306a36Sopenharmony_ci	 * Shadow RAM sector restrictions necessary when reading from the NVM.
19662306a36Sopenharmony_ci	 */
19762306a36Sopenharmony_ci	status = ice_read_flat_nvm(hw, offset * sizeof(u16), &bytes,
19862306a36Sopenharmony_ci				   (__force u8 *)&data_local, true);
19962306a36Sopenharmony_ci	if (status)
20062306a36Sopenharmony_ci		return status;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	*data = le16_to_cpu(data_local);
20362306a36Sopenharmony_ci	return 0;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci/**
20762306a36Sopenharmony_ci * ice_acquire_nvm - Generic request for acquiring the NVM ownership
20862306a36Sopenharmony_ci * @hw: pointer to the HW structure
20962306a36Sopenharmony_ci * @access: NVM access type (read or write)
21062306a36Sopenharmony_ci *
21162306a36Sopenharmony_ci * This function will request NVM ownership.
21262306a36Sopenharmony_ci */
21362306a36Sopenharmony_ciint ice_acquire_nvm(struct ice_hw *hw, enum ice_aq_res_access_type access)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	if (hw->flash.blank_nvm_mode)
21662306a36Sopenharmony_ci		return 0;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	return ice_acquire_res(hw, ICE_NVM_RES_ID, access, ICE_NVM_TIMEOUT);
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci/**
22262306a36Sopenharmony_ci * ice_release_nvm - Generic request for releasing the NVM ownership
22362306a36Sopenharmony_ci * @hw: pointer to the HW structure
22462306a36Sopenharmony_ci *
22562306a36Sopenharmony_ci * This function will release NVM ownership.
22662306a36Sopenharmony_ci */
22762306a36Sopenharmony_civoid ice_release_nvm(struct ice_hw *hw)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	if (hw->flash.blank_nvm_mode)
23062306a36Sopenharmony_ci		return;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	ice_release_res(hw, ICE_NVM_RES_ID);
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci/**
23662306a36Sopenharmony_ci * ice_get_flash_bank_offset - Get offset into requested flash bank
23762306a36Sopenharmony_ci * @hw: pointer to the HW structure
23862306a36Sopenharmony_ci * @bank: whether to read from the active or inactive flash bank
23962306a36Sopenharmony_ci * @module: the module to read from
24062306a36Sopenharmony_ci *
24162306a36Sopenharmony_ci * Based on the module, lookup the module offset from the beginning of the
24262306a36Sopenharmony_ci * flash.
24362306a36Sopenharmony_ci *
24462306a36Sopenharmony_ci * Returns the flash offset. Note that a value of zero is invalid and must be
24562306a36Sopenharmony_ci * treated as an error.
24662306a36Sopenharmony_ci */
24762306a36Sopenharmony_cistatic u32 ice_get_flash_bank_offset(struct ice_hw *hw, enum ice_bank_select bank, u16 module)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	struct ice_bank_info *banks = &hw->flash.banks;
25062306a36Sopenharmony_ci	enum ice_flash_bank active_bank;
25162306a36Sopenharmony_ci	bool second_bank_active;
25262306a36Sopenharmony_ci	u32 offset, size;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	switch (module) {
25562306a36Sopenharmony_ci	case ICE_SR_1ST_NVM_BANK_PTR:
25662306a36Sopenharmony_ci		offset = banks->nvm_ptr;
25762306a36Sopenharmony_ci		size = banks->nvm_size;
25862306a36Sopenharmony_ci		active_bank = banks->nvm_bank;
25962306a36Sopenharmony_ci		break;
26062306a36Sopenharmony_ci	case ICE_SR_1ST_OROM_BANK_PTR:
26162306a36Sopenharmony_ci		offset = banks->orom_ptr;
26262306a36Sopenharmony_ci		size = banks->orom_size;
26362306a36Sopenharmony_ci		active_bank = banks->orom_bank;
26462306a36Sopenharmony_ci		break;
26562306a36Sopenharmony_ci	case ICE_SR_NETLIST_BANK_PTR:
26662306a36Sopenharmony_ci		offset = banks->netlist_ptr;
26762306a36Sopenharmony_ci		size = banks->netlist_size;
26862306a36Sopenharmony_ci		active_bank = banks->netlist_bank;
26962306a36Sopenharmony_ci		break;
27062306a36Sopenharmony_ci	default:
27162306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Unexpected value for flash module: 0x%04x\n", module);
27262306a36Sopenharmony_ci		return 0;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	switch (active_bank) {
27662306a36Sopenharmony_ci	case ICE_1ST_FLASH_BANK:
27762306a36Sopenharmony_ci		second_bank_active = false;
27862306a36Sopenharmony_ci		break;
27962306a36Sopenharmony_ci	case ICE_2ND_FLASH_BANK:
28062306a36Sopenharmony_ci		second_bank_active = true;
28162306a36Sopenharmony_ci		break;
28262306a36Sopenharmony_ci	default:
28362306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Unexpected value for active flash bank: %u\n",
28462306a36Sopenharmony_ci			  active_bank);
28562306a36Sopenharmony_ci		return 0;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	/* The second flash bank is stored immediately following the first
28962306a36Sopenharmony_ci	 * bank. Based on whether the 1st or 2nd bank is active, and whether
29062306a36Sopenharmony_ci	 * we want the active or inactive bank, calculate the desired offset.
29162306a36Sopenharmony_ci	 */
29262306a36Sopenharmony_ci	switch (bank) {
29362306a36Sopenharmony_ci	case ICE_ACTIVE_FLASH_BANK:
29462306a36Sopenharmony_ci		return offset + (second_bank_active ? size : 0);
29562306a36Sopenharmony_ci	case ICE_INACTIVE_FLASH_BANK:
29662306a36Sopenharmony_ci		return offset + (second_bank_active ? 0 : size);
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	ice_debug(hw, ICE_DBG_NVM, "Unexpected value for flash bank selection: %u\n", bank);
30062306a36Sopenharmony_ci	return 0;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci/**
30462306a36Sopenharmony_ci * ice_read_flash_module - Read a word from one of the main NVM modules
30562306a36Sopenharmony_ci * @hw: pointer to the HW structure
30662306a36Sopenharmony_ci * @bank: which bank of the module to read
30762306a36Sopenharmony_ci * @module: the module to read
30862306a36Sopenharmony_ci * @offset: the offset into the module in bytes
30962306a36Sopenharmony_ci * @data: storage for the word read from the flash
31062306a36Sopenharmony_ci * @length: bytes of data to read
31162306a36Sopenharmony_ci *
31262306a36Sopenharmony_ci * Read data from the specified flash module. The bank parameter indicates
31362306a36Sopenharmony_ci * whether or not to read from the active bank or the inactive bank of that
31462306a36Sopenharmony_ci * module.
31562306a36Sopenharmony_ci *
31662306a36Sopenharmony_ci * The word will be read using flat NVM access, and relies on the
31762306a36Sopenharmony_ci * hw->flash.banks data being setup by ice_determine_active_flash_banks()
31862306a36Sopenharmony_ci * during initialization.
31962306a36Sopenharmony_ci */
32062306a36Sopenharmony_cistatic int
32162306a36Sopenharmony_ciice_read_flash_module(struct ice_hw *hw, enum ice_bank_select bank, u16 module,
32262306a36Sopenharmony_ci		      u32 offset, u8 *data, u32 length)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	int status;
32562306a36Sopenharmony_ci	u32 start;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	start = ice_get_flash_bank_offset(hw, bank, module);
32862306a36Sopenharmony_ci	if (!start) {
32962306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Unable to calculate flash bank offset for module 0x%04x\n",
33062306a36Sopenharmony_ci			  module);
33162306a36Sopenharmony_ci		return -EINVAL;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	status = ice_acquire_nvm(hw, ICE_RES_READ);
33562306a36Sopenharmony_ci	if (status)
33662306a36Sopenharmony_ci		return status;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	status = ice_read_flat_nvm(hw, start + offset, &length, data, false);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	ice_release_nvm(hw);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	return status;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci/**
34662306a36Sopenharmony_ci * ice_read_nvm_module - Read from the active main NVM module
34762306a36Sopenharmony_ci * @hw: pointer to the HW structure
34862306a36Sopenharmony_ci * @bank: whether to read from active or inactive NVM module
34962306a36Sopenharmony_ci * @offset: offset into the NVM module to read, in words
35062306a36Sopenharmony_ci * @data: storage for returned word value
35162306a36Sopenharmony_ci *
35262306a36Sopenharmony_ci * Read the specified word from the active NVM module. This includes the CSS
35362306a36Sopenharmony_ci * header at the start of the NVM module.
35462306a36Sopenharmony_ci */
35562306a36Sopenharmony_cistatic int
35662306a36Sopenharmony_ciice_read_nvm_module(struct ice_hw *hw, enum ice_bank_select bank, u32 offset, u16 *data)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	__le16 data_local;
35962306a36Sopenharmony_ci	int status;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	status = ice_read_flash_module(hw, bank, ICE_SR_1ST_NVM_BANK_PTR, offset * sizeof(u16),
36262306a36Sopenharmony_ci				       (__force u8 *)&data_local, sizeof(u16));
36362306a36Sopenharmony_ci	if (!status)
36462306a36Sopenharmony_ci		*data = le16_to_cpu(data_local);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	return status;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci/**
37062306a36Sopenharmony_ci * ice_read_nvm_sr_copy - Read a word from the Shadow RAM copy in the NVM bank
37162306a36Sopenharmony_ci * @hw: pointer to the HW structure
37262306a36Sopenharmony_ci * @bank: whether to read from the active or inactive NVM module
37362306a36Sopenharmony_ci * @offset: offset into the Shadow RAM copy to read, in words
37462306a36Sopenharmony_ci * @data: storage for returned word value
37562306a36Sopenharmony_ci *
37662306a36Sopenharmony_ci * Read the specified word from the copy of the Shadow RAM found in the
37762306a36Sopenharmony_ci * specified NVM module.
37862306a36Sopenharmony_ci */
37962306a36Sopenharmony_cistatic int
38062306a36Sopenharmony_ciice_read_nvm_sr_copy(struct ice_hw *hw, enum ice_bank_select bank, u32 offset, u16 *data)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	return ice_read_nvm_module(hw, bank, ICE_NVM_SR_COPY_WORD_OFFSET + offset, data);
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci/**
38662306a36Sopenharmony_ci * ice_read_netlist_module - Read data from the netlist module area
38762306a36Sopenharmony_ci * @hw: pointer to the HW structure
38862306a36Sopenharmony_ci * @bank: whether to read from the active or inactive module
38962306a36Sopenharmony_ci * @offset: offset into the netlist to read from
39062306a36Sopenharmony_ci * @data: storage for returned word value
39162306a36Sopenharmony_ci *
39262306a36Sopenharmony_ci * Read a word from the specified netlist bank.
39362306a36Sopenharmony_ci */
39462306a36Sopenharmony_cistatic int
39562306a36Sopenharmony_ciice_read_netlist_module(struct ice_hw *hw, enum ice_bank_select bank, u32 offset, u16 *data)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	__le16 data_local;
39862306a36Sopenharmony_ci	int status;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	status = ice_read_flash_module(hw, bank, ICE_SR_NETLIST_BANK_PTR, offset * sizeof(u16),
40162306a36Sopenharmony_ci				       (__force u8 *)&data_local, sizeof(u16));
40262306a36Sopenharmony_ci	if (!status)
40362306a36Sopenharmony_ci		*data = le16_to_cpu(data_local);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	return status;
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci/**
40962306a36Sopenharmony_ci * ice_read_sr_word - Reads Shadow RAM word and acquire NVM if necessary
41062306a36Sopenharmony_ci * @hw: pointer to the HW structure
41162306a36Sopenharmony_ci * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF)
41262306a36Sopenharmony_ci * @data: word read from the Shadow RAM
41362306a36Sopenharmony_ci *
41462306a36Sopenharmony_ci * Reads one 16 bit word from the Shadow RAM using the ice_read_sr_word_aq.
41562306a36Sopenharmony_ci */
41662306a36Sopenharmony_ciint ice_read_sr_word(struct ice_hw *hw, u16 offset, u16 *data)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	int status;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	status = ice_acquire_nvm(hw, ICE_RES_READ);
42162306a36Sopenharmony_ci	if (!status) {
42262306a36Sopenharmony_ci		status = ice_read_sr_word_aq(hw, offset, data);
42362306a36Sopenharmony_ci		ice_release_nvm(hw);
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	return status;
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci/**
43062306a36Sopenharmony_ci * ice_get_pfa_module_tlv - Reads sub module TLV from NVM PFA
43162306a36Sopenharmony_ci * @hw: pointer to hardware structure
43262306a36Sopenharmony_ci * @module_tlv: pointer to module TLV to return
43362306a36Sopenharmony_ci * @module_tlv_len: pointer to module TLV length to return
43462306a36Sopenharmony_ci * @module_type: module type requested
43562306a36Sopenharmony_ci *
43662306a36Sopenharmony_ci * Finds the requested sub module TLV type from the Preserved Field
43762306a36Sopenharmony_ci * Area (PFA) and returns the TLV pointer and length. The caller can
43862306a36Sopenharmony_ci * use these to read the variable length TLV value.
43962306a36Sopenharmony_ci */
44062306a36Sopenharmony_ciint
44162306a36Sopenharmony_ciice_get_pfa_module_tlv(struct ice_hw *hw, u16 *module_tlv, u16 *module_tlv_len,
44262306a36Sopenharmony_ci		       u16 module_type)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	u16 pfa_len, pfa_ptr;
44562306a36Sopenharmony_ci	u16 next_tlv;
44662306a36Sopenharmony_ci	int status;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	status = ice_read_sr_word(hw, ICE_SR_PFA_PTR, &pfa_ptr);
44962306a36Sopenharmony_ci	if (status) {
45062306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_INIT, "Preserved Field Array pointer.\n");
45162306a36Sopenharmony_ci		return status;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci	status = ice_read_sr_word(hw, pfa_ptr, &pfa_len);
45462306a36Sopenharmony_ci	if (status) {
45562306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_INIT, "Failed to read PFA length.\n");
45662306a36Sopenharmony_ci		return status;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci	/* Starting with first TLV after PFA length, iterate through the list
45962306a36Sopenharmony_ci	 * of TLVs to find the requested one.
46062306a36Sopenharmony_ci	 */
46162306a36Sopenharmony_ci	next_tlv = pfa_ptr + 1;
46262306a36Sopenharmony_ci	while (next_tlv < pfa_ptr + pfa_len) {
46362306a36Sopenharmony_ci		u16 tlv_sub_module_type;
46462306a36Sopenharmony_ci		u16 tlv_len;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci		/* Read TLV type */
46762306a36Sopenharmony_ci		status = ice_read_sr_word(hw, next_tlv, &tlv_sub_module_type);
46862306a36Sopenharmony_ci		if (status) {
46962306a36Sopenharmony_ci			ice_debug(hw, ICE_DBG_INIT, "Failed to read TLV type.\n");
47062306a36Sopenharmony_ci			break;
47162306a36Sopenharmony_ci		}
47262306a36Sopenharmony_ci		/* Read TLV length */
47362306a36Sopenharmony_ci		status = ice_read_sr_word(hw, next_tlv + 1, &tlv_len);
47462306a36Sopenharmony_ci		if (status) {
47562306a36Sopenharmony_ci			ice_debug(hw, ICE_DBG_INIT, "Failed to read TLV length.\n");
47662306a36Sopenharmony_ci			break;
47762306a36Sopenharmony_ci		}
47862306a36Sopenharmony_ci		if (tlv_sub_module_type == module_type) {
47962306a36Sopenharmony_ci			if (tlv_len) {
48062306a36Sopenharmony_ci				*module_tlv = next_tlv;
48162306a36Sopenharmony_ci				*module_tlv_len = tlv_len;
48262306a36Sopenharmony_ci				return 0;
48362306a36Sopenharmony_ci			}
48462306a36Sopenharmony_ci			return -EINVAL;
48562306a36Sopenharmony_ci		}
48662306a36Sopenharmony_ci		/* Check next TLV, i.e. current TLV pointer + length + 2 words
48762306a36Sopenharmony_ci		 * (for current TLV's type and length)
48862306a36Sopenharmony_ci		 */
48962306a36Sopenharmony_ci		next_tlv = next_tlv + tlv_len + 2;
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci	/* Module does not exist */
49262306a36Sopenharmony_ci	return -ENOENT;
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci/**
49662306a36Sopenharmony_ci * ice_read_pba_string - Reads part number string from NVM
49762306a36Sopenharmony_ci * @hw: pointer to hardware structure
49862306a36Sopenharmony_ci * @pba_num: stores the part number string from the NVM
49962306a36Sopenharmony_ci * @pba_num_size: part number string buffer length
50062306a36Sopenharmony_ci *
50162306a36Sopenharmony_ci * Reads the part number string from the NVM.
50262306a36Sopenharmony_ci */
50362306a36Sopenharmony_ciint ice_read_pba_string(struct ice_hw *hw, u8 *pba_num, u32 pba_num_size)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	u16 pba_tlv, pba_tlv_len;
50662306a36Sopenharmony_ci	u16 pba_word, pba_size;
50762306a36Sopenharmony_ci	int status;
50862306a36Sopenharmony_ci	u16 i;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	status = ice_get_pfa_module_tlv(hw, &pba_tlv, &pba_tlv_len,
51162306a36Sopenharmony_ci					ICE_SR_PBA_BLOCK_PTR);
51262306a36Sopenharmony_ci	if (status) {
51362306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_INIT, "Failed to read PBA Block TLV.\n");
51462306a36Sopenharmony_ci		return status;
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	/* pba_size is the next word */
51862306a36Sopenharmony_ci	status = ice_read_sr_word(hw, (pba_tlv + 2), &pba_size);
51962306a36Sopenharmony_ci	if (status) {
52062306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_INIT, "Failed to read PBA Section size.\n");
52162306a36Sopenharmony_ci		return status;
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	if (pba_tlv_len < pba_size) {
52562306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_INIT, "Invalid PBA Block TLV size.\n");
52662306a36Sopenharmony_ci		return -EINVAL;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	/* Subtract one to get PBA word count (PBA Size word is included in
53062306a36Sopenharmony_ci	 * total size)
53162306a36Sopenharmony_ci	 */
53262306a36Sopenharmony_ci	pba_size--;
53362306a36Sopenharmony_ci	if (pba_num_size < (((u32)pba_size * 2) + 1)) {
53462306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_INIT, "Buffer too small for PBA data.\n");
53562306a36Sopenharmony_ci		return -EINVAL;
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	for (i = 0; i < pba_size; i++) {
53962306a36Sopenharmony_ci		status = ice_read_sr_word(hw, (pba_tlv + 2 + 1) + i, &pba_word);
54062306a36Sopenharmony_ci		if (status) {
54162306a36Sopenharmony_ci			ice_debug(hw, ICE_DBG_INIT, "Failed to read PBA Block word %d.\n", i);
54262306a36Sopenharmony_ci			return status;
54362306a36Sopenharmony_ci		}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci		pba_num[(i * 2)] = (pba_word >> 8) & 0xFF;
54662306a36Sopenharmony_ci		pba_num[(i * 2) + 1] = pba_word & 0xFF;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci	pba_num[(pba_size * 2)] = '\0';
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	return status;
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci/**
55462306a36Sopenharmony_ci * ice_get_nvm_ver_info - Read NVM version information
55562306a36Sopenharmony_ci * @hw: pointer to the HW struct
55662306a36Sopenharmony_ci * @bank: whether to read from the active or inactive flash bank
55762306a36Sopenharmony_ci * @nvm: pointer to NVM info structure
55862306a36Sopenharmony_ci *
55962306a36Sopenharmony_ci * Read the NVM EETRACK ID and map version of the main NVM image bank, filling
56062306a36Sopenharmony_ci * in the NVM info structure.
56162306a36Sopenharmony_ci */
56262306a36Sopenharmony_cistatic int
56362306a36Sopenharmony_ciice_get_nvm_ver_info(struct ice_hw *hw, enum ice_bank_select bank, struct ice_nvm_info *nvm)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	u16 eetrack_lo, eetrack_hi, ver;
56662306a36Sopenharmony_ci	int status;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	status = ice_read_nvm_sr_copy(hw, bank, ICE_SR_NVM_DEV_STARTER_VER, &ver);
56962306a36Sopenharmony_ci	if (status) {
57062306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Failed to read DEV starter version.\n");
57162306a36Sopenharmony_ci		return status;
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	nvm->major = (ver & ICE_NVM_VER_HI_MASK) >> ICE_NVM_VER_HI_SHIFT;
57562306a36Sopenharmony_ci	nvm->minor = (ver & ICE_NVM_VER_LO_MASK) >> ICE_NVM_VER_LO_SHIFT;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	status = ice_read_nvm_sr_copy(hw, bank, ICE_SR_NVM_EETRACK_LO, &eetrack_lo);
57862306a36Sopenharmony_ci	if (status) {
57962306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Failed to read EETRACK lo.\n");
58062306a36Sopenharmony_ci		return status;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci	status = ice_read_nvm_sr_copy(hw, bank, ICE_SR_NVM_EETRACK_HI, &eetrack_hi);
58362306a36Sopenharmony_ci	if (status) {
58462306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Failed to read EETRACK hi.\n");
58562306a36Sopenharmony_ci		return status;
58662306a36Sopenharmony_ci	}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	nvm->eetrack = (eetrack_hi << 16) | eetrack_lo;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	return 0;
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci/**
59462306a36Sopenharmony_ci * ice_get_inactive_nvm_ver - Read Option ROM version from the inactive bank
59562306a36Sopenharmony_ci * @hw: pointer to the HW structure
59662306a36Sopenharmony_ci * @nvm: storage for Option ROM version information
59762306a36Sopenharmony_ci *
59862306a36Sopenharmony_ci * Reads the NVM EETRACK ID, Map version, and security revision of the
59962306a36Sopenharmony_ci * inactive NVM bank. Used to access version data for a pending update that
60062306a36Sopenharmony_ci * has not yet been activated.
60162306a36Sopenharmony_ci */
60262306a36Sopenharmony_ciint ice_get_inactive_nvm_ver(struct ice_hw *hw, struct ice_nvm_info *nvm)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	return ice_get_nvm_ver_info(hw, ICE_INACTIVE_FLASH_BANK, nvm);
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci/**
60862306a36Sopenharmony_ci * ice_get_orom_civd_data - Get the combo version information from Option ROM
60962306a36Sopenharmony_ci * @hw: pointer to the HW struct
61062306a36Sopenharmony_ci * @bank: whether to read from the active or inactive flash module
61162306a36Sopenharmony_ci * @civd: storage for the Option ROM CIVD data.
61262306a36Sopenharmony_ci *
61362306a36Sopenharmony_ci * Searches through the Option ROM flash contents to locate the CIVD data for
61462306a36Sopenharmony_ci * the image.
61562306a36Sopenharmony_ci */
61662306a36Sopenharmony_cistatic int
61762306a36Sopenharmony_ciice_get_orom_civd_data(struct ice_hw *hw, enum ice_bank_select bank,
61862306a36Sopenharmony_ci		       struct ice_orom_civd_info *civd)
61962306a36Sopenharmony_ci{
62062306a36Sopenharmony_ci	u8 *orom_data;
62162306a36Sopenharmony_ci	int status;
62262306a36Sopenharmony_ci	u32 offset;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	/* The CIVD section is located in the Option ROM aligned to 512 bytes.
62562306a36Sopenharmony_ci	 * The first 4 bytes must contain the ASCII characters "$CIV".
62662306a36Sopenharmony_ci	 * A simple modulo 256 sum of all of the bytes of the structure must
62762306a36Sopenharmony_ci	 * equal 0.
62862306a36Sopenharmony_ci	 *
62962306a36Sopenharmony_ci	 * The exact location is unknown and varies between images but is
63062306a36Sopenharmony_ci	 * usually somewhere in the middle of the bank. We need to scan the
63162306a36Sopenharmony_ci	 * Option ROM bank to locate it.
63262306a36Sopenharmony_ci	 *
63362306a36Sopenharmony_ci	 * It's significantly faster to read the entire Option ROM up front
63462306a36Sopenharmony_ci	 * using the maximum page size, than to read each possible location
63562306a36Sopenharmony_ci	 * with a separate firmware command.
63662306a36Sopenharmony_ci	 */
63762306a36Sopenharmony_ci	orom_data = vzalloc(hw->flash.banks.orom_size);
63862306a36Sopenharmony_ci	if (!orom_data)
63962306a36Sopenharmony_ci		return -ENOMEM;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	status = ice_read_flash_module(hw, bank, ICE_SR_1ST_OROM_BANK_PTR, 0,
64262306a36Sopenharmony_ci				       orom_data, hw->flash.banks.orom_size);
64362306a36Sopenharmony_ci	if (status) {
64462306a36Sopenharmony_ci		vfree(orom_data);
64562306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Unable to read Option ROM data\n");
64662306a36Sopenharmony_ci		return status;
64762306a36Sopenharmony_ci	}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	/* Scan the memory buffer to locate the CIVD data section */
65062306a36Sopenharmony_ci	for (offset = 0; (offset + 512) <= hw->flash.banks.orom_size; offset += 512) {
65162306a36Sopenharmony_ci		struct ice_orom_civd_info *tmp;
65262306a36Sopenharmony_ci		u8 sum = 0, i;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci		tmp = (struct ice_orom_civd_info *)&orom_data[offset];
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci		/* Skip forward until we find a matching signature */
65762306a36Sopenharmony_ci		if (memcmp("$CIV", tmp->signature, sizeof(tmp->signature)) != 0)
65862306a36Sopenharmony_ci			continue;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Found CIVD section at offset %u\n",
66162306a36Sopenharmony_ci			  offset);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci		/* Verify that the simple checksum is zero */
66462306a36Sopenharmony_ci		for (i = 0; i < sizeof(*tmp); i++)
66562306a36Sopenharmony_ci			sum += ((u8 *)tmp)[i];
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci		if (sum) {
66862306a36Sopenharmony_ci			ice_debug(hw, ICE_DBG_NVM, "Found CIVD data with invalid checksum of %u\n",
66962306a36Sopenharmony_ci				  sum);
67062306a36Sopenharmony_ci			goto err_invalid_checksum;
67162306a36Sopenharmony_ci		}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci		*civd = *tmp;
67462306a36Sopenharmony_ci		vfree(orom_data);
67562306a36Sopenharmony_ci		return 0;
67662306a36Sopenharmony_ci	}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	ice_debug(hw, ICE_DBG_NVM, "Unable to locate CIVD data within the Option ROM\n");
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_cierr_invalid_checksum:
68162306a36Sopenharmony_ci	vfree(orom_data);
68262306a36Sopenharmony_ci	return -EIO;
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci/**
68662306a36Sopenharmony_ci * ice_get_orom_ver_info - Read Option ROM version information
68762306a36Sopenharmony_ci * @hw: pointer to the HW struct
68862306a36Sopenharmony_ci * @bank: whether to read from the active or inactive flash module
68962306a36Sopenharmony_ci * @orom: pointer to Option ROM info structure
69062306a36Sopenharmony_ci *
69162306a36Sopenharmony_ci * Read Option ROM version and security revision from the Option ROM flash
69262306a36Sopenharmony_ci * section.
69362306a36Sopenharmony_ci */
69462306a36Sopenharmony_cistatic int
69562306a36Sopenharmony_ciice_get_orom_ver_info(struct ice_hw *hw, enum ice_bank_select bank, struct ice_orom_info *orom)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci	struct ice_orom_civd_info civd;
69862306a36Sopenharmony_ci	u32 combo_ver;
69962306a36Sopenharmony_ci	int status;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	status = ice_get_orom_civd_data(hw, bank, &civd);
70262306a36Sopenharmony_ci	if (status) {
70362306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Failed to locate valid Option ROM CIVD data\n");
70462306a36Sopenharmony_ci		return status;
70562306a36Sopenharmony_ci	}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	combo_ver = le32_to_cpu(civd.combo_ver);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	orom->major = (u8)((combo_ver & ICE_OROM_VER_MASK) >> ICE_OROM_VER_SHIFT);
71062306a36Sopenharmony_ci	orom->patch = (u8)(combo_ver & ICE_OROM_VER_PATCH_MASK);
71162306a36Sopenharmony_ci	orom->build = (u16)((combo_ver & ICE_OROM_VER_BUILD_MASK) >> ICE_OROM_VER_BUILD_SHIFT);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	return 0;
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci/**
71762306a36Sopenharmony_ci * ice_get_inactive_orom_ver - Read Option ROM version from the inactive bank
71862306a36Sopenharmony_ci * @hw: pointer to the HW structure
71962306a36Sopenharmony_ci * @orom: storage for Option ROM version information
72062306a36Sopenharmony_ci *
72162306a36Sopenharmony_ci * Reads the Option ROM version and security revision data for the inactive
72262306a36Sopenharmony_ci * section of flash. Used to access version data for a pending update that has
72362306a36Sopenharmony_ci * not yet been activated.
72462306a36Sopenharmony_ci */
72562306a36Sopenharmony_ciint ice_get_inactive_orom_ver(struct ice_hw *hw, struct ice_orom_info *orom)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	return ice_get_orom_ver_info(hw, ICE_INACTIVE_FLASH_BANK, orom);
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci/**
73162306a36Sopenharmony_ci * ice_get_netlist_info
73262306a36Sopenharmony_ci * @hw: pointer to the HW struct
73362306a36Sopenharmony_ci * @bank: whether to read from the active or inactive flash bank
73462306a36Sopenharmony_ci * @netlist: pointer to netlist version info structure
73562306a36Sopenharmony_ci *
73662306a36Sopenharmony_ci * Get the netlist version information from the requested bank. Reads the Link
73762306a36Sopenharmony_ci * Topology section to find the Netlist ID block and extract the relevant
73862306a36Sopenharmony_ci * information into the netlist version structure.
73962306a36Sopenharmony_ci */
74062306a36Sopenharmony_cistatic int
74162306a36Sopenharmony_ciice_get_netlist_info(struct ice_hw *hw, enum ice_bank_select bank,
74262306a36Sopenharmony_ci		     struct ice_netlist_info *netlist)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	u16 module_id, length, node_count, i;
74562306a36Sopenharmony_ci	u16 *id_blk;
74662306a36Sopenharmony_ci	int status;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	status = ice_read_netlist_module(hw, bank, ICE_NETLIST_TYPE_OFFSET, &module_id);
74962306a36Sopenharmony_ci	if (status)
75062306a36Sopenharmony_ci		return status;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	if (module_id != ICE_NETLIST_LINK_TOPO_MOD_ID) {
75362306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Expected netlist module_id ID of 0x%04x, but got 0x%04x\n",
75462306a36Sopenharmony_ci			  ICE_NETLIST_LINK_TOPO_MOD_ID, module_id);
75562306a36Sopenharmony_ci		return -EIO;
75662306a36Sopenharmony_ci	}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	status = ice_read_netlist_module(hw, bank, ICE_LINK_TOPO_MODULE_LEN, &length);
75962306a36Sopenharmony_ci	if (status)
76062306a36Sopenharmony_ci		return status;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	/* sanity check that we have at least enough words to store the netlist ID block */
76362306a36Sopenharmony_ci	if (length < ICE_NETLIST_ID_BLK_SIZE) {
76462306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Netlist Link Topology module too small. Expected at least %u words, but got %u words.\n",
76562306a36Sopenharmony_ci			  ICE_NETLIST_ID_BLK_SIZE, length);
76662306a36Sopenharmony_ci		return -EIO;
76762306a36Sopenharmony_ci	}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	status = ice_read_netlist_module(hw, bank, ICE_LINK_TOPO_NODE_COUNT, &node_count);
77062306a36Sopenharmony_ci	if (status)
77162306a36Sopenharmony_ci		return status;
77262306a36Sopenharmony_ci	node_count &= ICE_LINK_TOPO_NODE_COUNT_M;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	id_blk = kcalloc(ICE_NETLIST_ID_BLK_SIZE, sizeof(*id_blk), GFP_KERNEL);
77562306a36Sopenharmony_ci	if (!id_blk)
77662306a36Sopenharmony_ci		return -ENOMEM;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	/* Read out the entire Netlist ID Block at once. */
77962306a36Sopenharmony_ci	status = ice_read_flash_module(hw, bank, ICE_SR_NETLIST_BANK_PTR,
78062306a36Sopenharmony_ci				       ICE_NETLIST_ID_BLK_OFFSET(node_count) * sizeof(u16),
78162306a36Sopenharmony_ci				       (u8 *)id_blk, ICE_NETLIST_ID_BLK_SIZE * sizeof(u16));
78262306a36Sopenharmony_ci	if (status)
78362306a36Sopenharmony_ci		goto exit_error;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	for (i = 0; i < ICE_NETLIST_ID_BLK_SIZE; i++)
78662306a36Sopenharmony_ci		id_blk[i] = le16_to_cpu(((__force __le16 *)id_blk)[i]);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	netlist->major = id_blk[ICE_NETLIST_ID_BLK_MAJOR_VER_HIGH] << 16 |
78962306a36Sopenharmony_ci			 id_blk[ICE_NETLIST_ID_BLK_MAJOR_VER_LOW];
79062306a36Sopenharmony_ci	netlist->minor = id_blk[ICE_NETLIST_ID_BLK_MINOR_VER_HIGH] << 16 |
79162306a36Sopenharmony_ci			 id_blk[ICE_NETLIST_ID_BLK_MINOR_VER_LOW];
79262306a36Sopenharmony_ci	netlist->type = id_blk[ICE_NETLIST_ID_BLK_TYPE_HIGH] << 16 |
79362306a36Sopenharmony_ci			id_blk[ICE_NETLIST_ID_BLK_TYPE_LOW];
79462306a36Sopenharmony_ci	netlist->rev = id_blk[ICE_NETLIST_ID_BLK_REV_HIGH] << 16 |
79562306a36Sopenharmony_ci		       id_blk[ICE_NETLIST_ID_BLK_REV_LOW];
79662306a36Sopenharmony_ci	netlist->cust_ver = id_blk[ICE_NETLIST_ID_BLK_CUST_VER];
79762306a36Sopenharmony_ci	/* Read the left most 4 bytes of SHA */
79862306a36Sopenharmony_ci	netlist->hash = id_blk[ICE_NETLIST_ID_BLK_SHA_HASH_WORD(15)] << 16 |
79962306a36Sopenharmony_ci			id_blk[ICE_NETLIST_ID_BLK_SHA_HASH_WORD(14)];
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ciexit_error:
80262306a36Sopenharmony_ci	kfree(id_blk);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	return status;
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci/**
80862306a36Sopenharmony_ci * ice_get_inactive_netlist_ver
80962306a36Sopenharmony_ci * @hw: pointer to the HW struct
81062306a36Sopenharmony_ci * @netlist: pointer to netlist version info structure
81162306a36Sopenharmony_ci *
81262306a36Sopenharmony_ci * Read the netlist version data from the inactive netlist bank. Used to
81362306a36Sopenharmony_ci * extract version data of a pending flash update in order to display the
81462306a36Sopenharmony_ci * version data.
81562306a36Sopenharmony_ci */
81662306a36Sopenharmony_ciint ice_get_inactive_netlist_ver(struct ice_hw *hw, struct ice_netlist_info *netlist)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	return ice_get_netlist_info(hw, ICE_INACTIVE_FLASH_BANK, netlist);
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci/**
82262306a36Sopenharmony_ci * ice_discover_flash_size - Discover the available flash size.
82362306a36Sopenharmony_ci * @hw: pointer to the HW struct
82462306a36Sopenharmony_ci *
82562306a36Sopenharmony_ci * The device flash could be up to 16MB in size. However, it is possible that
82662306a36Sopenharmony_ci * the actual size is smaller. Use bisection to determine the accessible size
82762306a36Sopenharmony_ci * of flash memory.
82862306a36Sopenharmony_ci */
82962306a36Sopenharmony_cistatic int ice_discover_flash_size(struct ice_hw *hw)
83062306a36Sopenharmony_ci{
83162306a36Sopenharmony_ci	u32 min_size = 0, max_size = ICE_AQC_NVM_MAX_OFFSET + 1;
83262306a36Sopenharmony_ci	int status;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	status = ice_acquire_nvm(hw, ICE_RES_READ);
83562306a36Sopenharmony_ci	if (status)
83662306a36Sopenharmony_ci		return status;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	while ((max_size - min_size) > 1) {
83962306a36Sopenharmony_ci		u32 offset = (max_size + min_size) / 2;
84062306a36Sopenharmony_ci		u32 len = 1;
84162306a36Sopenharmony_ci		u8 data;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci		status = ice_read_flat_nvm(hw, offset, &len, &data, false);
84462306a36Sopenharmony_ci		if (status == -EIO &&
84562306a36Sopenharmony_ci		    hw->adminq.sq_last_status == ICE_AQ_RC_EINVAL) {
84662306a36Sopenharmony_ci			ice_debug(hw, ICE_DBG_NVM, "%s: New upper bound of %u bytes\n",
84762306a36Sopenharmony_ci				  __func__, offset);
84862306a36Sopenharmony_ci			status = 0;
84962306a36Sopenharmony_ci			max_size = offset;
85062306a36Sopenharmony_ci		} else if (!status) {
85162306a36Sopenharmony_ci			ice_debug(hw, ICE_DBG_NVM, "%s: New lower bound of %u bytes\n",
85262306a36Sopenharmony_ci				  __func__, offset);
85362306a36Sopenharmony_ci			min_size = offset;
85462306a36Sopenharmony_ci		} else {
85562306a36Sopenharmony_ci			/* an unexpected error occurred */
85662306a36Sopenharmony_ci			goto err_read_flat_nvm;
85762306a36Sopenharmony_ci		}
85862306a36Sopenharmony_ci	}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	ice_debug(hw, ICE_DBG_NVM, "Predicted flash size is %u bytes\n", max_size);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	hw->flash.flash_size = max_size;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_cierr_read_flat_nvm:
86562306a36Sopenharmony_ci	ice_release_nvm(hw);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	return status;
86862306a36Sopenharmony_ci}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci/**
87162306a36Sopenharmony_ci * ice_read_sr_pointer - Read the value of a Shadow RAM pointer word
87262306a36Sopenharmony_ci * @hw: pointer to the HW structure
87362306a36Sopenharmony_ci * @offset: the word offset of the Shadow RAM word to read
87462306a36Sopenharmony_ci * @pointer: pointer value read from Shadow RAM
87562306a36Sopenharmony_ci *
87662306a36Sopenharmony_ci * Read the given Shadow RAM word, and convert it to a pointer value specified
87762306a36Sopenharmony_ci * in bytes. This function assumes the specified offset is a valid pointer
87862306a36Sopenharmony_ci * word.
87962306a36Sopenharmony_ci *
88062306a36Sopenharmony_ci * Each pointer word specifies whether it is stored in word size or 4KB
88162306a36Sopenharmony_ci * sector size by using the highest bit. The reported pointer value will be in
88262306a36Sopenharmony_ci * bytes, intended for flat NVM reads.
88362306a36Sopenharmony_ci */
88462306a36Sopenharmony_cistatic int ice_read_sr_pointer(struct ice_hw *hw, u16 offset, u32 *pointer)
88562306a36Sopenharmony_ci{
88662306a36Sopenharmony_ci	int status;
88762306a36Sopenharmony_ci	u16 value;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	status = ice_read_sr_word(hw, offset, &value);
89062306a36Sopenharmony_ci	if (status)
89162306a36Sopenharmony_ci		return status;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	/* Determine if the pointer is in 4KB or word units */
89462306a36Sopenharmony_ci	if (value & ICE_SR_NVM_PTR_4KB_UNITS)
89562306a36Sopenharmony_ci		*pointer = (value & ~ICE_SR_NVM_PTR_4KB_UNITS) * 4 * 1024;
89662306a36Sopenharmony_ci	else
89762306a36Sopenharmony_ci		*pointer = value * 2;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	return 0;
90062306a36Sopenharmony_ci}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci/**
90362306a36Sopenharmony_ci * ice_read_sr_area_size - Read an area size from a Shadow RAM word
90462306a36Sopenharmony_ci * @hw: pointer to the HW structure
90562306a36Sopenharmony_ci * @offset: the word offset of the Shadow RAM to read
90662306a36Sopenharmony_ci * @size: size value read from the Shadow RAM
90762306a36Sopenharmony_ci *
90862306a36Sopenharmony_ci * Read the given Shadow RAM word, and convert it to an area size value
90962306a36Sopenharmony_ci * specified in bytes. This function assumes the specified offset is a valid
91062306a36Sopenharmony_ci * area size word.
91162306a36Sopenharmony_ci *
91262306a36Sopenharmony_ci * Each area size word is specified in 4KB sector units. This function reports
91362306a36Sopenharmony_ci * the size in bytes, intended for flat NVM reads.
91462306a36Sopenharmony_ci */
91562306a36Sopenharmony_cistatic int ice_read_sr_area_size(struct ice_hw *hw, u16 offset, u32 *size)
91662306a36Sopenharmony_ci{
91762306a36Sopenharmony_ci	int status;
91862306a36Sopenharmony_ci	u16 value;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	status = ice_read_sr_word(hw, offset, &value);
92162306a36Sopenharmony_ci	if (status)
92262306a36Sopenharmony_ci		return status;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	/* Area sizes are always specified in 4KB units */
92562306a36Sopenharmony_ci	*size = value * 4 * 1024;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	return 0;
92862306a36Sopenharmony_ci}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci/**
93162306a36Sopenharmony_ci * ice_determine_active_flash_banks - Discover active bank for each module
93262306a36Sopenharmony_ci * @hw: pointer to the HW struct
93362306a36Sopenharmony_ci *
93462306a36Sopenharmony_ci * Read the Shadow RAM control word and determine which banks are active for
93562306a36Sopenharmony_ci * the NVM, OROM, and Netlist modules. Also read and calculate the associated
93662306a36Sopenharmony_ci * pointer and size. These values are then cached into the ice_flash_info
93762306a36Sopenharmony_ci * structure for later use in order to calculate the correct offset to read
93862306a36Sopenharmony_ci * from the active module.
93962306a36Sopenharmony_ci */
94062306a36Sopenharmony_cistatic int ice_determine_active_flash_banks(struct ice_hw *hw)
94162306a36Sopenharmony_ci{
94262306a36Sopenharmony_ci	struct ice_bank_info *banks = &hw->flash.banks;
94362306a36Sopenharmony_ci	u16 ctrl_word;
94462306a36Sopenharmony_ci	int status;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	status = ice_read_sr_word(hw, ICE_SR_NVM_CTRL_WORD, &ctrl_word);
94762306a36Sopenharmony_ci	if (status) {
94862306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Failed to read the Shadow RAM control word\n");
94962306a36Sopenharmony_ci		return status;
95062306a36Sopenharmony_ci	}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	/* Check that the control word indicates validity */
95362306a36Sopenharmony_ci	if ((ctrl_word & ICE_SR_CTRL_WORD_1_M) >> ICE_SR_CTRL_WORD_1_S != ICE_SR_CTRL_WORD_VALID) {
95462306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Shadow RAM control word is invalid\n");
95562306a36Sopenharmony_ci		return -EIO;
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	if (!(ctrl_word & ICE_SR_CTRL_WORD_NVM_BANK))
95962306a36Sopenharmony_ci		banks->nvm_bank = ICE_1ST_FLASH_BANK;
96062306a36Sopenharmony_ci	else
96162306a36Sopenharmony_ci		banks->nvm_bank = ICE_2ND_FLASH_BANK;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	if (!(ctrl_word & ICE_SR_CTRL_WORD_OROM_BANK))
96462306a36Sopenharmony_ci		banks->orom_bank = ICE_1ST_FLASH_BANK;
96562306a36Sopenharmony_ci	else
96662306a36Sopenharmony_ci		banks->orom_bank = ICE_2ND_FLASH_BANK;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	if (!(ctrl_word & ICE_SR_CTRL_WORD_NETLIST_BANK))
96962306a36Sopenharmony_ci		banks->netlist_bank = ICE_1ST_FLASH_BANK;
97062306a36Sopenharmony_ci	else
97162306a36Sopenharmony_ci		banks->netlist_bank = ICE_2ND_FLASH_BANK;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	status = ice_read_sr_pointer(hw, ICE_SR_1ST_NVM_BANK_PTR, &banks->nvm_ptr);
97462306a36Sopenharmony_ci	if (status) {
97562306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Failed to read NVM bank pointer\n");
97662306a36Sopenharmony_ci		return status;
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	status = ice_read_sr_area_size(hw, ICE_SR_NVM_BANK_SIZE, &banks->nvm_size);
98062306a36Sopenharmony_ci	if (status) {
98162306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Failed to read NVM bank area size\n");
98262306a36Sopenharmony_ci		return status;
98362306a36Sopenharmony_ci	}
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	status = ice_read_sr_pointer(hw, ICE_SR_1ST_OROM_BANK_PTR, &banks->orom_ptr);
98662306a36Sopenharmony_ci	if (status) {
98762306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Failed to read OROM bank pointer\n");
98862306a36Sopenharmony_ci		return status;
98962306a36Sopenharmony_ci	}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	status = ice_read_sr_area_size(hw, ICE_SR_OROM_BANK_SIZE, &banks->orom_size);
99262306a36Sopenharmony_ci	if (status) {
99362306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Failed to read OROM bank area size\n");
99462306a36Sopenharmony_ci		return status;
99562306a36Sopenharmony_ci	}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	status = ice_read_sr_pointer(hw, ICE_SR_NETLIST_BANK_PTR, &banks->netlist_ptr);
99862306a36Sopenharmony_ci	if (status) {
99962306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Failed to read Netlist bank pointer\n");
100062306a36Sopenharmony_ci		return status;
100162306a36Sopenharmony_ci	}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	status = ice_read_sr_area_size(hw, ICE_SR_NETLIST_BANK_SIZE, &banks->netlist_size);
100462306a36Sopenharmony_ci	if (status) {
100562306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Failed to read Netlist bank area size\n");
100662306a36Sopenharmony_ci		return status;
100762306a36Sopenharmony_ci	}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	return 0;
101062306a36Sopenharmony_ci}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci/**
101362306a36Sopenharmony_ci * ice_init_nvm - initializes NVM setting
101462306a36Sopenharmony_ci * @hw: pointer to the HW struct
101562306a36Sopenharmony_ci *
101662306a36Sopenharmony_ci * This function reads and populates NVM settings such as Shadow RAM size,
101762306a36Sopenharmony_ci * max_timeout, and blank_nvm_mode
101862306a36Sopenharmony_ci */
101962306a36Sopenharmony_ciint ice_init_nvm(struct ice_hw *hw)
102062306a36Sopenharmony_ci{
102162306a36Sopenharmony_ci	struct ice_flash_info *flash = &hw->flash;
102262306a36Sopenharmony_ci	u32 fla, gens_stat;
102362306a36Sopenharmony_ci	u8 sr_size;
102462306a36Sopenharmony_ci	int status;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	/* The SR size is stored regardless of the NVM programming mode
102762306a36Sopenharmony_ci	 * as the blank mode may be used in the factory line.
102862306a36Sopenharmony_ci	 */
102962306a36Sopenharmony_ci	gens_stat = rd32(hw, GLNVM_GENS);
103062306a36Sopenharmony_ci	sr_size = (gens_stat & GLNVM_GENS_SR_SIZE_M) >> GLNVM_GENS_SR_SIZE_S;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	/* Switching to words (sr_size contains power of 2) */
103362306a36Sopenharmony_ci	flash->sr_words = BIT(sr_size) * ICE_SR_WORDS_IN_1KB;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	/* Check if we are in the normal or blank NVM programming mode */
103662306a36Sopenharmony_ci	fla = rd32(hw, GLNVM_FLA);
103762306a36Sopenharmony_ci	if (fla & GLNVM_FLA_LOCKED_M) { /* Normal programming mode */
103862306a36Sopenharmony_ci		flash->blank_nvm_mode = false;
103962306a36Sopenharmony_ci	} else {
104062306a36Sopenharmony_ci		/* Blank programming mode */
104162306a36Sopenharmony_ci		flash->blank_nvm_mode = true;
104262306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "NVM init error: unsupported blank mode.\n");
104362306a36Sopenharmony_ci		return -EIO;
104462306a36Sopenharmony_ci	}
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	status = ice_discover_flash_size(hw);
104762306a36Sopenharmony_ci	if (status) {
104862306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "NVM init error: failed to discover flash size.\n");
104962306a36Sopenharmony_ci		return status;
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	status = ice_determine_active_flash_banks(hw);
105362306a36Sopenharmony_ci	if (status) {
105462306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_NVM, "Failed to determine active flash banks.\n");
105562306a36Sopenharmony_ci		return status;
105662306a36Sopenharmony_ci	}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	status = ice_get_nvm_ver_info(hw, ICE_ACTIVE_FLASH_BANK, &flash->nvm);
105962306a36Sopenharmony_ci	if (status) {
106062306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_INIT, "Failed to read NVM info.\n");
106162306a36Sopenharmony_ci		return status;
106262306a36Sopenharmony_ci	}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	status = ice_get_orom_ver_info(hw, ICE_ACTIVE_FLASH_BANK, &flash->orom);
106562306a36Sopenharmony_ci	if (status)
106662306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_INIT, "Failed to read Option ROM info.\n");
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	/* read the netlist version information */
106962306a36Sopenharmony_ci	status = ice_get_netlist_info(hw, ICE_ACTIVE_FLASH_BANK, &flash->netlist);
107062306a36Sopenharmony_ci	if (status)
107162306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_INIT, "Failed to read netlist info.\n");
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	return 0;
107462306a36Sopenharmony_ci}
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci/**
107762306a36Sopenharmony_ci * ice_nvm_validate_checksum
107862306a36Sopenharmony_ci * @hw: pointer to the HW struct
107962306a36Sopenharmony_ci *
108062306a36Sopenharmony_ci * Verify NVM PFA checksum validity (0x0706)
108162306a36Sopenharmony_ci */
108262306a36Sopenharmony_ciint ice_nvm_validate_checksum(struct ice_hw *hw)
108362306a36Sopenharmony_ci{
108462306a36Sopenharmony_ci	struct ice_aqc_nvm_checksum *cmd;
108562306a36Sopenharmony_ci	struct ice_aq_desc desc;
108662306a36Sopenharmony_ci	int status;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	status = ice_acquire_nvm(hw, ICE_RES_READ);
108962306a36Sopenharmony_ci	if (status)
109062306a36Sopenharmony_ci		return status;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	cmd = &desc.params.nvm_checksum;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_nvm_checksum);
109562306a36Sopenharmony_ci	cmd->flags = ICE_AQC_NVM_CHECKSUM_VERIFY;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
109862306a36Sopenharmony_ci	ice_release_nvm(hw);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	if (!status)
110162306a36Sopenharmony_ci		if (le16_to_cpu(cmd->checksum) != ICE_AQC_NVM_CHECKSUM_CORRECT)
110262306a36Sopenharmony_ci			status = -EIO;
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	return status;
110562306a36Sopenharmony_ci}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci/**
110862306a36Sopenharmony_ci * ice_nvm_write_activate
110962306a36Sopenharmony_ci * @hw: pointer to the HW struct
111062306a36Sopenharmony_ci * @cmd_flags: flags for write activate command
111162306a36Sopenharmony_ci * @response_flags: response indicators from firmware
111262306a36Sopenharmony_ci *
111362306a36Sopenharmony_ci * Update the control word with the required banks' validity bits
111462306a36Sopenharmony_ci * and dumps the Shadow RAM to flash (0x0707)
111562306a36Sopenharmony_ci *
111662306a36Sopenharmony_ci * cmd_flags controls which banks to activate, the preservation level to use
111762306a36Sopenharmony_ci * when activating the NVM bank, and whether an EMP reset is required for
111862306a36Sopenharmony_ci * activation.
111962306a36Sopenharmony_ci *
112062306a36Sopenharmony_ci * Note that the 16bit cmd_flags value is split between two separate 1 byte
112162306a36Sopenharmony_ci * flag values in the descriptor.
112262306a36Sopenharmony_ci *
112362306a36Sopenharmony_ci * On successful return of the firmware command, the response_flags variable
112462306a36Sopenharmony_ci * is updated with the flags reported by firmware indicating certain status,
112562306a36Sopenharmony_ci * such as whether EMP reset is enabled.
112662306a36Sopenharmony_ci */
112762306a36Sopenharmony_ciint ice_nvm_write_activate(struct ice_hw *hw, u16 cmd_flags, u8 *response_flags)
112862306a36Sopenharmony_ci{
112962306a36Sopenharmony_ci	struct ice_aqc_nvm *cmd;
113062306a36Sopenharmony_ci	struct ice_aq_desc desc;
113162306a36Sopenharmony_ci	int err;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	cmd = &desc.params.nvm;
113462306a36Sopenharmony_ci	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_nvm_write_activate);
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	cmd->cmd_flags = (u8)(cmd_flags & 0xFF);
113762306a36Sopenharmony_ci	cmd->offset_high = (u8)((cmd_flags >> 8) & 0xFF);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	err = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
114062306a36Sopenharmony_ci	if (!err && response_flags)
114162306a36Sopenharmony_ci		*response_flags = cmd->cmd_flags;
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	return err;
114462306a36Sopenharmony_ci}
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci/**
114762306a36Sopenharmony_ci * ice_aq_nvm_update_empr
114862306a36Sopenharmony_ci * @hw: pointer to the HW struct
114962306a36Sopenharmony_ci *
115062306a36Sopenharmony_ci * Update empr (0x0709). This command allows SW to
115162306a36Sopenharmony_ci * request an EMPR to activate new FW.
115262306a36Sopenharmony_ci */
115362306a36Sopenharmony_ciint ice_aq_nvm_update_empr(struct ice_hw *hw)
115462306a36Sopenharmony_ci{
115562306a36Sopenharmony_ci	struct ice_aq_desc desc;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_nvm_update_empr);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
116062306a36Sopenharmony_ci}
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci/* ice_nvm_set_pkg_data
116362306a36Sopenharmony_ci * @hw: pointer to the HW struct
116462306a36Sopenharmony_ci * @del_pkg_data_flag: If is set then the current pkg_data store by FW
116562306a36Sopenharmony_ci *		       is deleted.
116662306a36Sopenharmony_ci *		       If bit is set to 1, then buffer should be size 0.
116762306a36Sopenharmony_ci * @data: pointer to buffer
116862306a36Sopenharmony_ci * @length: length of the buffer
116962306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
117062306a36Sopenharmony_ci *
117162306a36Sopenharmony_ci * Set package data (0x070A). This command is equivalent to the reception
117262306a36Sopenharmony_ci * of a PLDM FW Update GetPackageData cmd. This command should be sent
117362306a36Sopenharmony_ci * as part of the NVM update as the first cmd in the flow.
117462306a36Sopenharmony_ci */
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ciint
117762306a36Sopenharmony_ciice_nvm_set_pkg_data(struct ice_hw *hw, bool del_pkg_data_flag, u8 *data,
117862306a36Sopenharmony_ci		     u16 length, struct ice_sq_cd *cd)
117962306a36Sopenharmony_ci{
118062306a36Sopenharmony_ci	struct ice_aqc_nvm_pkg_data *cmd;
118162306a36Sopenharmony_ci	struct ice_aq_desc desc;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	if (length != 0 && !data)
118462306a36Sopenharmony_ci		return -EINVAL;
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	cmd = &desc.params.pkg_data;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_nvm_pkg_data);
118962306a36Sopenharmony_ci	desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	if (del_pkg_data_flag)
119262306a36Sopenharmony_ci		cmd->cmd_flags |= ICE_AQC_NVM_PKG_DELETE;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	return ice_aq_send_cmd(hw, &desc, data, length, cd);
119562306a36Sopenharmony_ci}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci/* ice_nvm_pass_component_tbl
119862306a36Sopenharmony_ci * @hw: pointer to the HW struct
119962306a36Sopenharmony_ci * @data: pointer to buffer
120062306a36Sopenharmony_ci * @length: length of the buffer
120162306a36Sopenharmony_ci * @transfer_flag: parameter for determining stage of the update
120262306a36Sopenharmony_ci * @comp_response: a pointer to the response from the 0x070B AQC.
120362306a36Sopenharmony_ci * @comp_response_code: a pointer to the response code from the 0x070B AQC.
120462306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
120562306a36Sopenharmony_ci *
120662306a36Sopenharmony_ci * Pass component table (0x070B). This command is equivalent to the reception
120762306a36Sopenharmony_ci * of a PLDM FW Update PassComponentTable cmd. This command should be sent once
120862306a36Sopenharmony_ci * per component. It can be only sent after Set Package Data cmd and before
120962306a36Sopenharmony_ci * actual update. FW will assume these commands are going to be sent until
121062306a36Sopenharmony_ci * the TransferFlag is set to End or StartAndEnd.
121162306a36Sopenharmony_ci */
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ciint
121462306a36Sopenharmony_ciice_nvm_pass_component_tbl(struct ice_hw *hw, u8 *data, u16 length,
121562306a36Sopenharmony_ci			   u8 transfer_flag, u8 *comp_response,
121662306a36Sopenharmony_ci			   u8 *comp_response_code, struct ice_sq_cd *cd)
121762306a36Sopenharmony_ci{
121862306a36Sopenharmony_ci	struct ice_aqc_nvm_pass_comp_tbl *cmd;
121962306a36Sopenharmony_ci	struct ice_aq_desc desc;
122062306a36Sopenharmony_ci	int status;
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	if (!data || !comp_response || !comp_response_code)
122362306a36Sopenharmony_ci		return -EINVAL;
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	cmd = &desc.params.pass_comp_tbl;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	ice_fill_dflt_direct_cmd_desc(&desc,
122862306a36Sopenharmony_ci				      ice_aqc_opc_nvm_pass_component_tbl);
122962306a36Sopenharmony_ci	desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	cmd->transfer_flag = transfer_flag;
123262306a36Sopenharmony_ci	status = ice_aq_send_cmd(hw, &desc, data, length, cd);
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	if (!status) {
123562306a36Sopenharmony_ci		*comp_response = cmd->component_response;
123662306a36Sopenharmony_ci		*comp_response_code = cmd->component_response_code;
123762306a36Sopenharmony_ci	}
123862306a36Sopenharmony_ci	return status;
123962306a36Sopenharmony_ci}
1240