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