162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright(c) 2013 - 2018 Intel Corporation. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include "i40e_prototype.h" 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci/** 762306a36Sopenharmony_ci * i40e_init_nvm - Initialize NVM function pointers 862306a36Sopenharmony_ci * @hw: pointer to the HW structure 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Setup the function pointers and the NVM info structure. Should be called 1162306a36Sopenharmony_ci * once per NVM initialization, e.g. inside the i40e_init_shared_code(). 1262306a36Sopenharmony_ci * Please notice that the NVM term is used here (& in all methods covered 1362306a36Sopenharmony_ci * in this file) as an equivalent of the FLASH part mapped into the SR. 1462306a36Sopenharmony_ci * We are accessing FLASH always thru the Shadow RAM. 1562306a36Sopenharmony_ci **/ 1662306a36Sopenharmony_ciint i40e_init_nvm(struct i40e_hw *hw) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci struct i40e_nvm_info *nvm = &hw->nvm; 1962306a36Sopenharmony_ci int ret_code = 0; 2062306a36Sopenharmony_ci u32 fla, gens; 2162306a36Sopenharmony_ci u8 sr_size; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci /* The SR size is stored regardless of the nvm programming mode 2462306a36Sopenharmony_ci * as the blank mode may be used in the factory line. 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci gens = rd32(hw, I40E_GLNVM_GENS); 2762306a36Sopenharmony_ci sr_size = ((gens & I40E_GLNVM_GENS_SR_SIZE_MASK) >> 2862306a36Sopenharmony_ci I40E_GLNVM_GENS_SR_SIZE_SHIFT); 2962306a36Sopenharmony_ci /* Switching to words (sr_size contains power of 2KB) */ 3062306a36Sopenharmony_ci nvm->sr_size = BIT(sr_size) * I40E_SR_WORDS_IN_1KB; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci /* Check if we are in the normal or blank NVM programming mode */ 3362306a36Sopenharmony_ci fla = rd32(hw, I40E_GLNVM_FLA); 3462306a36Sopenharmony_ci if (fla & I40E_GLNVM_FLA_LOCKED_MASK) { /* Normal programming mode */ 3562306a36Sopenharmony_ci /* Max NVM timeout */ 3662306a36Sopenharmony_ci nvm->timeout = I40E_MAX_NVM_TIMEOUT; 3762306a36Sopenharmony_ci nvm->blank_nvm_mode = false; 3862306a36Sopenharmony_ci } else { /* Blank programming mode */ 3962306a36Sopenharmony_ci nvm->blank_nvm_mode = true; 4062306a36Sopenharmony_ci ret_code = -EIO; 4162306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, "NVM init error: unsupported blank mode.\n"); 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci return ret_code; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/** 4862306a36Sopenharmony_ci * i40e_acquire_nvm - Generic request for acquiring the NVM ownership 4962306a36Sopenharmony_ci * @hw: pointer to the HW structure 5062306a36Sopenharmony_ci * @access: NVM access type (read or write) 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * This function will request NVM ownership for reading 5362306a36Sopenharmony_ci * via the proper Admin Command. 5462306a36Sopenharmony_ci **/ 5562306a36Sopenharmony_ciint i40e_acquire_nvm(struct i40e_hw *hw, 5662306a36Sopenharmony_ci enum i40e_aq_resource_access_type access) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci u64 gtime, timeout; 5962306a36Sopenharmony_ci u64 time_left = 0; 6062306a36Sopenharmony_ci int ret_code = 0; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (hw->nvm.blank_nvm_mode) 6362306a36Sopenharmony_ci goto i40e_i40e_acquire_nvm_exit; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci ret_code = i40e_aq_request_resource(hw, I40E_NVM_RESOURCE_ID, access, 6662306a36Sopenharmony_ci 0, &time_left, NULL); 6762306a36Sopenharmony_ci /* Reading the Global Device Timer */ 6862306a36Sopenharmony_ci gtime = rd32(hw, I40E_GLVFGEN_TIMER); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* Store the timeout */ 7162306a36Sopenharmony_ci hw->nvm.hw_semaphore_timeout = I40E_MS_TO_GTIME(time_left) + gtime; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (ret_code) 7462306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 7562306a36Sopenharmony_ci "NVM acquire type %d failed time_left=%llu ret=%d aq_err=%d\n", 7662306a36Sopenharmony_ci access, time_left, ret_code, hw->aq.asq_last_status); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (ret_code && time_left) { 7962306a36Sopenharmony_ci /* Poll until the current NVM owner timeouts */ 8062306a36Sopenharmony_ci timeout = I40E_MS_TO_GTIME(I40E_MAX_NVM_TIMEOUT) + gtime; 8162306a36Sopenharmony_ci while ((gtime < timeout) && time_left) { 8262306a36Sopenharmony_ci usleep_range(10000, 20000); 8362306a36Sopenharmony_ci gtime = rd32(hw, I40E_GLVFGEN_TIMER); 8462306a36Sopenharmony_ci ret_code = i40e_aq_request_resource(hw, 8562306a36Sopenharmony_ci I40E_NVM_RESOURCE_ID, 8662306a36Sopenharmony_ci access, 0, &time_left, 8762306a36Sopenharmony_ci NULL); 8862306a36Sopenharmony_ci if (!ret_code) { 8962306a36Sopenharmony_ci hw->nvm.hw_semaphore_timeout = 9062306a36Sopenharmony_ci I40E_MS_TO_GTIME(time_left) + gtime; 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci if (ret_code) { 9562306a36Sopenharmony_ci hw->nvm.hw_semaphore_timeout = 0; 9662306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 9762306a36Sopenharmony_ci "NVM acquire timed out, wait %llu ms before trying again. status=%d aq_err=%d\n", 9862306a36Sopenharmony_ci time_left, ret_code, hw->aq.asq_last_status); 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cii40e_i40e_acquire_nvm_exit: 10362306a36Sopenharmony_ci return ret_code; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/** 10762306a36Sopenharmony_ci * i40e_release_nvm - Generic request for releasing the NVM ownership 10862306a36Sopenharmony_ci * @hw: pointer to the HW structure 10962306a36Sopenharmony_ci * 11062306a36Sopenharmony_ci * This function will release NVM resource via the proper Admin Command. 11162306a36Sopenharmony_ci **/ 11262306a36Sopenharmony_civoid i40e_release_nvm(struct i40e_hw *hw) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci u32 total_delay = 0; 11562306a36Sopenharmony_ci int ret_code = 0; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (hw->nvm.blank_nvm_mode) 11862306a36Sopenharmony_ci return; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci ret_code = i40e_aq_release_resource(hw, I40E_NVM_RESOURCE_ID, 0, NULL); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* there are some rare cases when trying to release the resource 12362306a36Sopenharmony_ci * results in an admin Q timeout, so handle them correctly 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ci while ((ret_code == -EIO) && 12662306a36Sopenharmony_ci (total_delay < hw->aq.asq_cmd_timeout)) { 12762306a36Sopenharmony_ci usleep_range(1000, 2000); 12862306a36Sopenharmony_ci ret_code = i40e_aq_release_resource(hw, 12962306a36Sopenharmony_ci I40E_NVM_RESOURCE_ID, 13062306a36Sopenharmony_ci 0, NULL); 13162306a36Sopenharmony_ci total_delay++; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/** 13662306a36Sopenharmony_ci * i40e_poll_sr_srctl_done_bit - Polls the GLNVM_SRCTL done bit 13762306a36Sopenharmony_ci * @hw: pointer to the HW structure 13862306a36Sopenharmony_ci * 13962306a36Sopenharmony_ci * Polls the SRCTL Shadow RAM register done bit. 14062306a36Sopenharmony_ci **/ 14162306a36Sopenharmony_cistatic int i40e_poll_sr_srctl_done_bit(struct i40e_hw *hw) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci int ret_code = -EIO; 14462306a36Sopenharmony_ci u32 srctl, wait_cnt; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* Poll the I40E_GLNVM_SRCTL until the done bit is set */ 14762306a36Sopenharmony_ci for (wait_cnt = 0; wait_cnt < I40E_SRRD_SRCTL_ATTEMPTS; wait_cnt++) { 14862306a36Sopenharmony_ci srctl = rd32(hw, I40E_GLNVM_SRCTL); 14962306a36Sopenharmony_ci if (srctl & I40E_GLNVM_SRCTL_DONE_MASK) { 15062306a36Sopenharmony_ci ret_code = 0; 15162306a36Sopenharmony_ci break; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci udelay(5); 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci if (ret_code == -EIO) 15662306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, "Done bit in GLNVM_SRCTL not set"); 15762306a36Sopenharmony_ci return ret_code; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/** 16162306a36Sopenharmony_ci * i40e_read_nvm_word_srctl - Reads Shadow RAM via SRCTL register 16262306a36Sopenharmony_ci * @hw: pointer to the HW structure 16362306a36Sopenharmony_ci * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) 16462306a36Sopenharmony_ci * @data: word read from the Shadow RAM 16562306a36Sopenharmony_ci * 16662306a36Sopenharmony_ci * Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register. 16762306a36Sopenharmony_ci **/ 16862306a36Sopenharmony_cistatic int i40e_read_nvm_word_srctl(struct i40e_hw *hw, u16 offset, 16962306a36Sopenharmony_ci u16 *data) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci int ret_code = -EIO; 17262306a36Sopenharmony_ci u32 sr_reg; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (offset >= hw->nvm.sr_size) { 17562306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 17662306a36Sopenharmony_ci "NVM read error: offset %d beyond Shadow RAM limit %d\n", 17762306a36Sopenharmony_ci offset, hw->nvm.sr_size); 17862306a36Sopenharmony_ci ret_code = -EINVAL; 17962306a36Sopenharmony_ci goto read_nvm_exit; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* Poll the done bit first */ 18362306a36Sopenharmony_ci ret_code = i40e_poll_sr_srctl_done_bit(hw); 18462306a36Sopenharmony_ci if (!ret_code) { 18562306a36Sopenharmony_ci /* Write the address and start reading */ 18662306a36Sopenharmony_ci sr_reg = ((u32)offset << I40E_GLNVM_SRCTL_ADDR_SHIFT) | 18762306a36Sopenharmony_ci BIT(I40E_GLNVM_SRCTL_START_SHIFT); 18862306a36Sopenharmony_ci wr32(hw, I40E_GLNVM_SRCTL, sr_reg); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* Poll I40E_GLNVM_SRCTL until the done bit is set */ 19162306a36Sopenharmony_ci ret_code = i40e_poll_sr_srctl_done_bit(hw); 19262306a36Sopenharmony_ci if (!ret_code) { 19362306a36Sopenharmony_ci sr_reg = rd32(hw, I40E_GLNVM_SRDATA); 19462306a36Sopenharmony_ci *data = (u16)((sr_reg & 19562306a36Sopenharmony_ci I40E_GLNVM_SRDATA_RDDATA_MASK) 19662306a36Sopenharmony_ci >> I40E_GLNVM_SRDATA_RDDATA_SHIFT); 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci if (ret_code) 20062306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 20162306a36Sopenharmony_ci "NVM read error: Couldn't access Shadow RAM address: 0x%x\n", 20262306a36Sopenharmony_ci offset); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ciread_nvm_exit: 20562306a36Sopenharmony_ci return ret_code; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/** 20962306a36Sopenharmony_ci * i40e_read_nvm_aq - Read Shadow RAM. 21062306a36Sopenharmony_ci * @hw: pointer to the HW structure. 21162306a36Sopenharmony_ci * @module_pointer: module pointer location in words from the NVM beginning 21262306a36Sopenharmony_ci * @offset: offset in words from module start 21362306a36Sopenharmony_ci * @words: number of words to read 21462306a36Sopenharmony_ci * @data: buffer with words to read to the Shadow RAM 21562306a36Sopenharmony_ci * @last_command: tells the AdminQ that this is the last command 21662306a36Sopenharmony_ci * 21762306a36Sopenharmony_ci * Reads a 16 bit words buffer to the Shadow RAM using the admin command. 21862306a36Sopenharmony_ci **/ 21962306a36Sopenharmony_cistatic int i40e_read_nvm_aq(struct i40e_hw *hw, 22062306a36Sopenharmony_ci u8 module_pointer, u32 offset, 22162306a36Sopenharmony_ci u16 words, void *data, 22262306a36Sopenharmony_ci bool last_command) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct i40e_asq_cmd_details cmd_details; 22562306a36Sopenharmony_ci int ret_code = -EIO; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci memset(&cmd_details, 0, sizeof(cmd_details)); 22862306a36Sopenharmony_ci cmd_details.wb_desc = &hw->nvm_wb_desc; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* Here we are checking the SR limit only for the flat memory model. 23162306a36Sopenharmony_ci * We cannot do it for the module-based model, as we did not acquire 23262306a36Sopenharmony_ci * the NVM resource yet (we cannot get the module pointer value). 23362306a36Sopenharmony_ci * Firmware will check the module-based model. 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_ci if ((offset + words) > hw->nvm.sr_size) 23662306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 23762306a36Sopenharmony_ci "NVM read error: offset %d beyond Shadow RAM limit %d\n", 23862306a36Sopenharmony_ci (offset + words), hw->nvm.sr_size); 23962306a36Sopenharmony_ci else if (words > I40E_SR_SECTOR_SIZE_IN_WORDS) 24062306a36Sopenharmony_ci /* We can read only up to 4KB (one sector), in one AQ write */ 24162306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 24262306a36Sopenharmony_ci "NVM read fail error: tried to read %d words, limit is %d.\n", 24362306a36Sopenharmony_ci words, I40E_SR_SECTOR_SIZE_IN_WORDS); 24462306a36Sopenharmony_ci else if (((offset + (words - 1)) / I40E_SR_SECTOR_SIZE_IN_WORDS) 24562306a36Sopenharmony_ci != (offset / I40E_SR_SECTOR_SIZE_IN_WORDS)) 24662306a36Sopenharmony_ci /* A single read cannot spread over two sectors */ 24762306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 24862306a36Sopenharmony_ci "NVM read error: cannot spread over two sectors in a single read offset=%d words=%d\n", 24962306a36Sopenharmony_ci offset, words); 25062306a36Sopenharmony_ci else 25162306a36Sopenharmony_ci ret_code = i40e_aq_read_nvm(hw, module_pointer, 25262306a36Sopenharmony_ci 2 * offset, /*bytes*/ 25362306a36Sopenharmony_ci 2 * words, /*bytes*/ 25462306a36Sopenharmony_ci data, last_command, &cmd_details); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return ret_code; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci/** 26062306a36Sopenharmony_ci * i40e_read_nvm_word_aq - Reads Shadow RAM via AQ 26162306a36Sopenharmony_ci * @hw: pointer to the HW structure 26262306a36Sopenharmony_ci * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) 26362306a36Sopenharmony_ci * @data: word read from the Shadow RAM 26462306a36Sopenharmony_ci * 26562306a36Sopenharmony_ci * Reads one 16 bit word from the Shadow RAM using the AdminQ 26662306a36Sopenharmony_ci **/ 26762306a36Sopenharmony_cistatic int i40e_read_nvm_word_aq(struct i40e_hw *hw, u16 offset, 26862306a36Sopenharmony_ci u16 *data) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci int ret_code = -EIO; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci ret_code = i40e_read_nvm_aq(hw, 0x0, offset, 1, data, true); 27362306a36Sopenharmony_ci *data = le16_to_cpu(*(__le16 *)data); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return ret_code; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci/** 27962306a36Sopenharmony_ci * __i40e_read_nvm_word - Reads nvm word, assumes caller does the locking 28062306a36Sopenharmony_ci * @hw: pointer to the HW structure 28162306a36Sopenharmony_ci * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) 28262306a36Sopenharmony_ci * @data: word read from the Shadow RAM 28362306a36Sopenharmony_ci * 28462306a36Sopenharmony_ci * Reads one 16 bit word from the Shadow RAM. 28562306a36Sopenharmony_ci * 28662306a36Sopenharmony_ci * Do not use this function except in cases where the nvm lock is already 28762306a36Sopenharmony_ci * taken via i40e_acquire_nvm(). 28862306a36Sopenharmony_ci **/ 28962306a36Sopenharmony_cistatic int __i40e_read_nvm_word(struct i40e_hw *hw, 29062306a36Sopenharmony_ci u16 offset, u16 *data) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) 29362306a36Sopenharmony_ci return i40e_read_nvm_word_aq(hw, offset, data); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci return i40e_read_nvm_word_srctl(hw, offset, data); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci/** 29962306a36Sopenharmony_ci * i40e_read_nvm_word - Reads nvm word and acquire lock if necessary 30062306a36Sopenharmony_ci * @hw: pointer to the HW structure 30162306a36Sopenharmony_ci * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) 30262306a36Sopenharmony_ci * @data: word read from the Shadow RAM 30362306a36Sopenharmony_ci * 30462306a36Sopenharmony_ci * Reads one 16 bit word from the Shadow RAM. 30562306a36Sopenharmony_ci **/ 30662306a36Sopenharmony_ciint i40e_read_nvm_word(struct i40e_hw *hw, u16 offset, 30762306a36Sopenharmony_ci u16 *data) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci int ret_code = 0; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (hw->flags & I40E_HW_FLAG_NVM_READ_REQUIRES_LOCK) 31262306a36Sopenharmony_ci ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); 31362306a36Sopenharmony_ci if (ret_code) 31462306a36Sopenharmony_ci return ret_code; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ret_code = __i40e_read_nvm_word(hw, offset, data); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (hw->flags & I40E_HW_FLAG_NVM_READ_REQUIRES_LOCK) 31962306a36Sopenharmony_ci i40e_release_nvm(hw); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return ret_code; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci/** 32562306a36Sopenharmony_ci * i40e_read_nvm_module_data - Reads NVM Buffer to specified memory location 32662306a36Sopenharmony_ci * @hw: Pointer to the HW structure 32762306a36Sopenharmony_ci * @module_ptr: Pointer to module in words with respect to NVM beginning 32862306a36Sopenharmony_ci * @module_offset: Offset in words from module start 32962306a36Sopenharmony_ci * @data_offset: Offset in words from reading data area start 33062306a36Sopenharmony_ci * @words_data_size: Words to read from NVM 33162306a36Sopenharmony_ci * @data_ptr: Pointer to memory location where resulting buffer will be stored 33262306a36Sopenharmony_ci **/ 33362306a36Sopenharmony_ciint i40e_read_nvm_module_data(struct i40e_hw *hw, 33462306a36Sopenharmony_ci u8 module_ptr, 33562306a36Sopenharmony_ci u16 module_offset, 33662306a36Sopenharmony_ci u16 data_offset, 33762306a36Sopenharmony_ci u16 words_data_size, 33862306a36Sopenharmony_ci u16 *data_ptr) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci u16 specific_ptr = 0; 34162306a36Sopenharmony_ci u16 ptr_value = 0; 34262306a36Sopenharmony_ci u32 offset = 0; 34362306a36Sopenharmony_ci int status; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (module_ptr != 0) { 34662306a36Sopenharmony_ci status = i40e_read_nvm_word(hw, module_ptr, &ptr_value); 34762306a36Sopenharmony_ci if (status) { 34862306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_ALL, 34962306a36Sopenharmony_ci "Reading nvm word failed.Error code: %d.\n", 35062306a36Sopenharmony_ci status); 35162306a36Sopenharmony_ci return -EIO; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci#define I40E_NVM_INVALID_PTR_VAL 0x7FFF 35562306a36Sopenharmony_ci#define I40E_NVM_INVALID_VAL 0xFFFF 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* Pointer not initialized */ 35862306a36Sopenharmony_ci if (ptr_value == I40E_NVM_INVALID_PTR_VAL || 35962306a36Sopenharmony_ci ptr_value == I40E_NVM_INVALID_VAL) { 36062306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_ALL, "Pointer not initialized.\n"); 36162306a36Sopenharmony_ci return -EINVAL; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* Check whether the module is in SR mapped area or outside */ 36562306a36Sopenharmony_ci if (ptr_value & I40E_PTR_TYPE) { 36662306a36Sopenharmony_ci /* Pointer points outside of the Shared RAM mapped area */ 36762306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_ALL, 36862306a36Sopenharmony_ci "Reading nvm data failed. Pointer points outside of the Shared RAM mapped area.\n"); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci return -EINVAL; 37162306a36Sopenharmony_ci } else { 37262306a36Sopenharmony_ci /* Read from the Shadow RAM */ 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci status = i40e_read_nvm_word(hw, ptr_value + module_offset, 37562306a36Sopenharmony_ci &specific_ptr); 37662306a36Sopenharmony_ci if (status) { 37762306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_ALL, 37862306a36Sopenharmony_ci "Reading nvm word failed.Error code: %d.\n", 37962306a36Sopenharmony_ci status); 38062306a36Sopenharmony_ci return -EIO; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci offset = ptr_value + module_offset + specific_ptr + 38462306a36Sopenharmony_ci data_offset; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci status = i40e_read_nvm_buffer(hw, offset, &words_data_size, 38762306a36Sopenharmony_ci data_ptr); 38862306a36Sopenharmony_ci if (status) { 38962306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_ALL, 39062306a36Sopenharmony_ci "Reading nvm buffer failed.Error code: %d.\n", 39162306a36Sopenharmony_ci status); 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return status; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci/** 39962306a36Sopenharmony_ci * i40e_read_nvm_buffer_srctl - Reads Shadow RAM buffer via SRCTL register 40062306a36Sopenharmony_ci * @hw: pointer to the HW structure 40162306a36Sopenharmony_ci * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF). 40262306a36Sopenharmony_ci * @words: (in) number of words to read; (out) number of words actually read 40362306a36Sopenharmony_ci * @data: words read from the Shadow RAM 40462306a36Sopenharmony_ci * 40562306a36Sopenharmony_ci * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd() 40662306a36Sopenharmony_ci * method. The buffer read is preceded by the NVM ownership take 40762306a36Sopenharmony_ci * and followed by the release. 40862306a36Sopenharmony_ci **/ 40962306a36Sopenharmony_cistatic int i40e_read_nvm_buffer_srctl(struct i40e_hw *hw, u16 offset, 41062306a36Sopenharmony_ci u16 *words, u16 *data) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci int ret_code = 0; 41362306a36Sopenharmony_ci u16 index, word; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* Loop thru the selected region */ 41662306a36Sopenharmony_ci for (word = 0; word < *words; word++) { 41762306a36Sopenharmony_ci index = offset + word; 41862306a36Sopenharmony_ci ret_code = i40e_read_nvm_word_srctl(hw, index, &data[word]); 41962306a36Sopenharmony_ci if (ret_code) 42062306a36Sopenharmony_ci break; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* Update the number of words read from the Shadow RAM */ 42462306a36Sopenharmony_ci *words = word; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return ret_code; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci/** 43062306a36Sopenharmony_ci * i40e_read_nvm_buffer_aq - Reads Shadow RAM buffer via AQ 43162306a36Sopenharmony_ci * @hw: pointer to the HW structure 43262306a36Sopenharmony_ci * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF). 43362306a36Sopenharmony_ci * @words: (in) number of words to read; (out) number of words actually read 43462306a36Sopenharmony_ci * @data: words read from the Shadow RAM 43562306a36Sopenharmony_ci * 43662306a36Sopenharmony_ci * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_aq() 43762306a36Sopenharmony_ci * method. The buffer read is preceded by the NVM ownership take 43862306a36Sopenharmony_ci * and followed by the release. 43962306a36Sopenharmony_ci **/ 44062306a36Sopenharmony_cistatic int i40e_read_nvm_buffer_aq(struct i40e_hw *hw, u16 offset, 44162306a36Sopenharmony_ci u16 *words, u16 *data) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci bool last_cmd = false; 44462306a36Sopenharmony_ci u16 words_read = 0; 44562306a36Sopenharmony_ci u16 read_size; 44662306a36Sopenharmony_ci int ret_code; 44762306a36Sopenharmony_ci u16 i = 0; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci do { 45062306a36Sopenharmony_ci /* Calculate number of bytes we should read in this step. 45162306a36Sopenharmony_ci * FVL AQ do not allow to read more than one page at a time or 45262306a36Sopenharmony_ci * to cross page boundaries. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_ci if (offset % I40E_SR_SECTOR_SIZE_IN_WORDS) 45562306a36Sopenharmony_ci read_size = min(*words, 45662306a36Sopenharmony_ci (u16)(I40E_SR_SECTOR_SIZE_IN_WORDS - 45762306a36Sopenharmony_ci (offset % I40E_SR_SECTOR_SIZE_IN_WORDS))); 45862306a36Sopenharmony_ci else 45962306a36Sopenharmony_ci read_size = min((*words - words_read), 46062306a36Sopenharmony_ci I40E_SR_SECTOR_SIZE_IN_WORDS); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* Check if this is last command, if so set proper flag */ 46362306a36Sopenharmony_ci if ((words_read + read_size) >= *words) 46462306a36Sopenharmony_ci last_cmd = true; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci ret_code = i40e_read_nvm_aq(hw, 0x0, offset, read_size, 46762306a36Sopenharmony_ci data + words_read, last_cmd); 46862306a36Sopenharmony_ci if (ret_code) 46962306a36Sopenharmony_ci goto read_nvm_buffer_aq_exit; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* Increment counter for words already read and move offset to 47262306a36Sopenharmony_ci * new read location 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_ci words_read += read_size; 47562306a36Sopenharmony_ci offset += read_size; 47662306a36Sopenharmony_ci } while (words_read < *words); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci for (i = 0; i < *words; i++) 47962306a36Sopenharmony_ci data[i] = le16_to_cpu(((__le16 *)data)[i]); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ciread_nvm_buffer_aq_exit: 48262306a36Sopenharmony_ci *words = words_read; 48362306a36Sopenharmony_ci return ret_code; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci/** 48762306a36Sopenharmony_ci * __i40e_read_nvm_buffer - Reads nvm buffer, caller must acquire lock 48862306a36Sopenharmony_ci * @hw: pointer to the HW structure 48962306a36Sopenharmony_ci * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF). 49062306a36Sopenharmony_ci * @words: (in) number of words to read; (out) number of words actually read 49162306a36Sopenharmony_ci * @data: words read from the Shadow RAM 49262306a36Sopenharmony_ci * 49362306a36Sopenharmony_ci * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd() 49462306a36Sopenharmony_ci * method. 49562306a36Sopenharmony_ci **/ 49662306a36Sopenharmony_cistatic int __i40e_read_nvm_buffer(struct i40e_hw *hw, 49762306a36Sopenharmony_ci u16 offset, u16 *words, 49862306a36Sopenharmony_ci u16 *data) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) 50162306a36Sopenharmony_ci return i40e_read_nvm_buffer_aq(hw, offset, words, data); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci return i40e_read_nvm_buffer_srctl(hw, offset, words, data); 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci/** 50762306a36Sopenharmony_ci * i40e_read_nvm_buffer - Reads Shadow RAM buffer and acquire lock if necessary 50862306a36Sopenharmony_ci * @hw: pointer to the HW structure 50962306a36Sopenharmony_ci * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF). 51062306a36Sopenharmony_ci * @words: (in) number of words to read; (out) number of words actually read 51162306a36Sopenharmony_ci * @data: words read from the Shadow RAM 51262306a36Sopenharmony_ci * 51362306a36Sopenharmony_ci * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd() 51462306a36Sopenharmony_ci * method. The buffer read is preceded by the NVM ownership take 51562306a36Sopenharmony_ci * and followed by the release. 51662306a36Sopenharmony_ci **/ 51762306a36Sopenharmony_ciint i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset, 51862306a36Sopenharmony_ci u16 *words, u16 *data) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci int ret_code = 0; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) { 52362306a36Sopenharmony_ci ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); 52462306a36Sopenharmony_ci if (!ret_code) { 52562306a36Sopenharmony_ci ret_code = i40e_read_nvm_buffer_aq(hw, offset, words, 52662306a36Sopenharmony_ci data); 52762306a36Sopenharmony_ci i40e_release_nvm(hw); 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci } else { 53062306a36Sopenharmony_ci ret_code = i40e_read_nvm_buffer_srctl(hw, offset, words, data); 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return ret_code; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci/** 53762306a36Sopenharmony_ci * i40e_write_nvm_aq - Writes Shadow RAM. 53862306a36Sopenharmony_ci * @hw: pointer to the HW structure. 53962306a36Sopenharmony_ci * @module_pointer: module pointer location in words from the NVM beginning 54062306a36Sopenharmony_ci * @offset: offset in words from module start 54162306a36Sopenharmony_ci * @words: number of words to write 54262306a36Sopenharmony_ci * @data: buffer with words to write to the Shadow RAM 54362306a36Sopenharmony_ci * @last_command: tells the AdminQ that this is the last command 54462306a36Sopenharmony_ci * 54562306a36Sopenharmony_ci * Writes a 16 bit words buffer to the Shadow RAM using the admin command. 54662306a36Sopenharmony_ci **/ 54762306a36Sopenharmony_cistatic int i40e_write_nvm_aq(struct i40e_hw *hw, u8 module_pointer, 54862306a36Sopenharmony_ci u32 offset, u16 words, void *data, 54962306a36Sopenharmony_ci bool last_command) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct i40e_asq_cmd_details cmd_details; 55262306a36Sopenharmony_ci int ret_code = -EIO; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci memset(&cmd_details, 0, sizeof(cmd_details)); 55562306a36Sopenharmony_ci cmd_details.wb_desc = &hw->nvm_wb_desc; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* Here we are checking the SR limit only for the flat memory model. 55862306a36Sopenharmony_ci * We cannot do it for the module-based model, as we did not acquire 55962306a36Sopenharmony_ci * the NVM resource yet (we cannot get the module pointer value). 56062306a36Sopenharmony_ci * Firmware will check the module-based model. 56162306a36Sopenharmony_ci */ 56262306a36Sopenharmony_ci if ((offset + words) > hw->nvm.sr_size) 56362306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 56462306a36Sopenharmony_ci "NVM write error: offset %d beyond Shadow RAM limit %d\n", 56562306a36Sopenharmony_ci (offset + words), hw->nvm.sr_size); 56662306a36Sopenharmony_ci else if (words > I40E_SR_SECTOR_SIZE_IN_WORDS) 56762306a36Sopenharmony_ci /* We can write only up to 4KB (one sector), in one AQ write */ 56862306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 56962306a36Sopenharmony_ci "NVM write fail error: tried to write %d words, limit is %d.\n", 57062306a36Sopenharmony_ci words, I40E_SR_SECTOR_SIZE_IN_WORDS); 57162306a36Sopenharmony_ci else if (((offset + (words - 1)) / I40E_SR_SECTOR_SIZE_IN_WORDS) 57262306a36Sopenharmony_ci != (offset / I40E_SR_SECTOR_SIZE_IN_WORDS)) 57362306a36Sopenharmony_ci /* A single write cannot spread over two sectors */ 57462306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 57562306a36Sopenharmony_ci "NVM write error: cannot spread over two sectors in a single write offset=%d words=%d\n", 57662306a36Sopenharmony_ci offset, words); 57762306a36Sopenharmony_ci else 57862306a36Sopenharmony_ci ret_code = i40e_aq_update_nvm(hw, module_pointer, 57962306a36Sopenharmony_ci 2 * offset, /*bytes*/ 58062306a36Sopenharmony_ci 2 * words, /*bytes*/ 58162306a36Sopenharmony_ci data, last_command, 0, 58262306a36Sopenharmony_ci &cmd_details); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci return ret_code; 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci/** 58862306a36Sopenharmony_ci * i40e_calc_nvm_checksum - Calculates and returns the checksum 58962306a36Sopenharmony_ci * @hw: pointer to hardware structure 59062306a36Sopenharmony_ci * @checksum: pointer to the checksum 59162306a36Sopenharmony_ci * 59262306a36Sopenharmony_ci * This function calculates SW Checksum that covers the whole 64kB shadow RAM 59362306a36Sopenharmony_ci * except the VPD and PCIe ALT Auto-load modules. The structure and size of VPD 59462306a36Sopenharmony_ci * is customer specific and unknown. Therefore, this function skips all maximum 59562306a36Sopenharmony_ci * possible size of VPD (1kB). 59662306a36Sopenharmony_ci **/ 59762306a36Sopenharmony_cistatic int i40e_calc_nvm_checksum(struct i40e_hw *hw, 59862306a36Sopenharmony_ci u16 *checksum) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct i40e_virt_mem vmem; 60162306a36Sopenharmony_ci u16 pcie_alt_module = 0; 60262306a36Sopenharmony_ci u16 checksum_local = 0; 60362306a36Sopenharmony_ci u16 vpd_module = 0; 60462306a36Sopenharmony_ci int ret_code; 60562306a36Sopenharmony_ci u16 *data; 60662306a36Sopenharmony_ci u16 i = 0; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci ret_code = i40e_allocate_virt_mem(hw, &vmem, 60962306a36Sopenharmony_ci I40E_SR_SECTOR_SIZE_IN_WORDS * sizeof(u16)); 61062306a36Sopenharmony_ci if (ret_code) 61162306a36Sopenharmony_ci goto i40e_calc_nvm_checksum_exit; 61262306a36Sopenharmony_ci data = (u16 *)vmem.va; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* read pointer to VPD area */ 61562306a36Sopenharmony_ci ret_code = __i40e_read_nvm_word(hw, I40E_SR_VPD_PTR, &vpd_module); 61662306a36Sopenharmony_ci if (ret_code) { 61762306a36Sopenharmony_ci ret_code = -EIO; 61862306a36Sopenharmony_ci goto i40e_calc_nvm_checksum_exit; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* read pointer to PCIe Alt Auto-load module */ 62262306a36Sopenharmony_ci ret_code = __i40e_read_nvm_word(hw, I40E_SR_PCIE_ALT_AUTO_LOAD_PTR, 62362306a36Sopenharmony_ci &pcie_alt_module); 62462306a36Sopenharmony_ci if (ret_code) { 62562306a36Sopenharmony_ci ret_code = -EIO; 62662306a36Sopenharmony_ci goto i40e_calc_nvm_checksum_exit; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci /* Calculate SW checksum that covers the whole 64kB shadow RAM 63062306a36Sopenharmony_ci * except the VPD and PCIe ALT Auto-load modules 63162306a36Sopenharmony_ci */ 63262306a36Sopenharmony_ci for (i = 0; i < hw->nvm.sr_size; i++) { 63362306a36Sopenharmony_ci /* Read SR page */ 63462306a36Sopenharmony_ci if ((i % I40E_SR_SECTOR_SIZE_IN_WORDS) == 0) { 63562306a36Sopenharmony_ci u16 words = I40E_SR_SECTOR_SIZE_IN_WORDS; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci ret_code = __i40e_read_nvm_buffer(hw, i, &words, data); 63862306a36Sopenharmony_ci if (ret_code) { 63962306a36Sopenharmony_ci ret_code = -EIO; 64062306a36Sopenharmony_ci goto i40e_calc_nvm_checksum_exit; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci /* Skip Checksum word */ 64562306a36Sopenharmony_ci if (i == I40E_SR_SW_CHECKSUM_WORD) 64662306a36Sopenharmony_ci continue; 64762306a36Sopenharmony_ci /* Skip VPD module (convert byte size to word count) */ 64862306a36Sopenharmony_ci if ((i >= (u32)vpd_module) && 64962306a36Sopenharmony_ci (i < ((u32)vpd_module + 65062306a36Sopenharmony_ci (I40E_SR_VPD_MODULE_MAX_SIZE / 2)))) { 65162306a36Sopenharmony_ci continue; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci /* Skip PCIe ALT module (convert byte size to word count) */ 65462306a36Sopenharmony_ci if ((i >= (u32)pcie_alt_module) && 65562306a36Sopenharmony_ci (i < ((u32)pcie_alt_module + 65662306a36Sopenharmony_ci (I40E_SR_PCIE_ALT_MODULE_MAX_SIZE / 2)))) { 65762306a36Sopenharmony_ci continue; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci checksum_local += data[i % I40E_SR_SECTOR_SIZE_IN_WORDS]; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci *checksum = (u16)I40E_SR_SW_CHECKSUM_BASE - checksum_local; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cii40e_calc_nvm_checksum_exit: 66662306a36Sopenharmony_ci i40e_free_virt_mem(hw, &vmem); 66762306a36Sopenharmony_ci return ret_code; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci/** 67162306a36Sopenharmony_ci * i40e_update_nvm_checksum - Updates the NVM checksum 67262306a36Sopenharmony_ci * @hw: pointer to hardware structure 67362306a36Sopenharmony_ci * 67462306a36Sopenharmony_ci * NVM ownership must be acquired before calling this function and released 67562306a36Sopenharmony_ci * on ARQ completion event reception by caller. 67662306a36Sopenharmony_ci * This function will commit SR to NVM. 67762306a36Sopenharmony_ci **/ 67862306a36Sopenharmony_ciint i40e_update_nvm_checksum(struct i40e_hw *hw) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci __le16 le_sum; 68162306a36Sopenharmony_ci int ret_code; 68262306a36Sopenharmony_ci u16 checksum; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci ret_code = i40e_calc_nvm_checksum(hw, &checksum); 68562306a36Sopenharmony_ci if (!ret_code) { 68662306a36Sopenharmony_ci le_sum = cpu_to_le16(checksum); 68762306a36Sopenharmony_ci ret_code = i40e_write_nvm_aq(hw, 0x00, I40E_SR_SW_CHECKSUM_WORD, 68862306a36Sopenharmony_ci 1, &le_sum, true); 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci return ret_code; 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci/** 69562306a36Sopenharmony_ci * i40e_validate_nvm_checksum - Validate EEPROM checksum 69662306a36Sopenharmony_ci * @hw: pointer to hardware structure 69762306a36Sopenharmony_ci * @checksum: calculated checksum 69862306a36Sopenharmony_ci * 69962306a36Sopenharmony_ci * Performs checksum calculation and validates the NVM SW checksum. If the 70062306a36Sopenharmony_ci * caller does not need checksum, the value can be NULL. 70162306a36Sopenharmony_ci **/ 70262306a36Sopenharmony_ciint i40e_validate_nvm_checksum(struct i40e_hw *hw, 70362306a36Sopenharmony_ci u16 *checksum) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci u16 checksum_local = 0; 70662306a36Sopenharmony_ci u16 checksum_sr = 0; 70762306a36Sopenharmony_ci int ret_code = 0; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci /* We must acquire the NVM lock in order to correctly synchronize the 71062306a36Sopenharmony_ci * NVM accesses across multiple PFs. Without doing so it is possible 71162306a36Sopenharmony_ci * for one of the PFs to read invalid data potentially indicating that 71262306a36Sopenharmony_ci * the checksum is invalid. 71362306a36Sopenharmony_ci */ 71462306a36Sopenharmony_ci ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); 71562306a36Sopenharmony_ci if (ret_code) 71662306a36Sopenharmony_ci return ret_code; 71762306a36Sopenharmony_ci ret_code = i40e_calc_nvm_checksum(hw, &checksum_local); 71862306a36Sopenharmony_ci __i40e_read_nvm_word(hw, I40E_SR_SW_CHECKSUM_WORD, &checksum_sr); 71962306a36Sopenharmony_ci i40e_release_nvm(hw); 72062306a36Sopenharmony_ci if (ret_code) 72162306a36Sopenharmony_ci return ret_code; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* Verify read checksum from EEPROM is the same as 72462306a36Sopenharmony_ci * calculated checksum 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_ci if (checksum_local != checksum_sr) 72762306a36Sopenharmony_ci ret_code = -EIO; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* If the user cares, return the calculated checksum */ 73062306a36Sopenharmony_ci if (checksum) 73162306a36Sopenharmony_ci *checksum = checksum_local; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci return ret_code; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic int i40e_nvmupd_state_init(struct i40e_hw *hw, 73762306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 73862306a36Sopenharmony_ci u8 *bytes, int *perrno); 73962306a36Sopenharmony_cistatic int i40e_nvmupd_state_reading(struct i40e_hw *hw, 74062306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 74162306a36Sopenharmony_ci u8 *bytes, int *perrno); 74262306a36Sopenharmony_cistatic int i40e_nvmupd_state_writing(struct i40e_hw *hw, 74362306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 74462306a36Sopenharmony_ci u8 *bytes, int *errno); 74562306a36Sopenharmony_cistatic enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, 74662306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 74762306a36Sopenharmony_ci int *perrno); 74862306a36Sopenharmony_cistatic int i40e_nvmupd_nvm_erase(struct i40e_hw *hw, 74962306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 75062306a36Sopenharmony_ci int *perrno); 75162306a36Sopenharmony_cistatic int i40e_nvmupd_nvm_write(struct i40e_hw *hw, 75262306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 75362306a36Sopenharmony_ci u8 *bytes, int *perrno); 75462306a36Sopenharmony_cistatic int i40e_nvmupd_nvm_read(struct i40e_hw *hw, 75562306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 75662306a36Sopenharmony_ci u8 *bytes, int *perrno); 75762306a36Sopenharmony_cistatic int i40e_nvmupd_exec_aq(struct i40e_hw *hw, 75862306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 75962306a36Sopenharmony_ci u8 *bytes, int *perrno); 76062306a36Sopenharmony_cistatic int i40e_nvmupd_get_aq_result(struct i40e_hw *hw, 76162306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 76262306a36Sopenharmony_ci u8 *bytes, int *perrno); 76362306a36Sopenharmony_cistatic int i40e_nvmupd_get_aq_event(struct i40e_hw *hw, 76462306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 76562306a36Sopenharmony_ci u8 *bytes, int *perrno); 76662306a36Sopenharmony_cistatic inline u8 i40e_nvmupd_get_module(u32 val) 76762306a36Sopenharmony_ci{ 76862306a36Sopenharmony_ci return (u8)(val & I40E_NVM_MOD_PNT_MASK); 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_cistatic inline u8 i40e_nvmupd_get_transaction(u32 val) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci return (u8)((val & I40E_NVM_TRANS_MASK) >> I40E_NVM_TRANS_SHIFT); 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cistatic inline u8 i40e_nvmupd_get_preservation_flags(u32 val) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci return (u8)((val & I40E_NVM_PRESERVATION_FLAGS_MASK) >> 77862306a36Sopenharmony_ci I40E_NVM_PRESERVATION_FLAGS_SHIFT); 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic const char * const i40e_nvm_update_state_str[] = { 78262306a36Sopenharmony_ci "I40E_NVMUPD_INVALID", 78362306a36Sopenharmony_ci "I40E_NVMUPD_READ_CON", 78462306a36Sopenharmony_ci "I40E_NVMUPD_READ_SNT", 78562306a36Sopenharmony_ci "I40E_NVMUPD_READ_LCB", 78662306a36Sopenharmony_ci "I40E_NVMUPD_READ_SA", 78762306a36Sopenharmony_ci "I40E_NVMUPD_WRITE_ERA", 78862306a36Sopenharmony_ci "I40E_NVMUPD_WRITE_CON", 78962306a36Sopenharmony_ci "I40E_NVMUPD_WRITE_SNT", 79062306a36Sopenharmony_ci "I40E_NVMUPD_WRITE_LCB", 79162306a36Sopenharmony_ci "I40E_NVMUPD_WRITE_SA", 79262306a36Sopenharmony_ci "I40E_NVMUPD_CSUM_CON", 79362306a36Sopenharmony_ci "I40E_NVMUPD_CSUM_SA", 79462306a36Sopenharmony_ci "I40E_NVMUPD_CSUM_LCB", 79562306a36Sopenharmony_ci "I40E_NVMUPD_STATUS", 79662306a36Sopenharmony_ci "I40E_NVMUPD_EXEC_AQ", 79762306a36Sopenharmony_ci "I40E_NVMUPD_GET_AQ_RESULT", 79862306a36Sopenharmony_ci "I40E_NVMUPD_GET_AQ_EVENT", 79962306a36Sopenharmony_ci}; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci/** 80262306a36Sopenharmony_ci * i40e_nvmupd_command - Process an NVM update command 80362306a36Sopenharmony_ci * @hw: pointer to hardware structure 80462306a36Sopenharmony_ci * @cmd: pointer to nvm update command 80562306a36Sopenharmony_ci * @bytes: pointer to the data buffer 80662306a36Sopenharmony_ci * @perrno: pointer to return error code 80762306a36Sopenharmony_ci * 80862306a36Sopenharmony_ci * Dispatches command depending on what update state is current 80962306a36Sopenharmony_ci **/ 81062306a36Sopenharmony_ciint i40e_nvmupd_command(struct i40e_hw *hw, 81162306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 81262306a36Sopenharmony_ci u8 *bytes, int *perrno) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci enum i40e_nvmupd_cmd upd_cmd; 81562306a36Sopenharmony_ci int status; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci /* assume success */ 81862306a36Sopenharmony_ci *perrno = 0; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* early check for status command and debug msgs */ 82162306a36Sopenharmony_ci upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, "%s state %d nvm_release_on_hold %d opc 0x%04x cmd 0x%08x config 0x%08x offset 0x%08x data_size 0x%08x\n", 82462306a36Sopenharmony_ci i40e_nvm_update_state_str[upd_cmd], 82562306a36Sopenharmony_ci hw->nvmupd_state, 82662306a36Sopenharmony_ci hw->nvm_release_on_done, hw->nvm_wait_opcode, 82762306a36Sopenharmony_ci cmd->command, cmd->config, cmd->offset, cmd->data_size); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci if (upd_cmd == I40E_NVMUPD_INVALID) { 83062306a36Sopenharmony_ci *perrno = -EFAULT; 83162306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 83262306a36Sopenharmony_ci "i40e_nvmupd_validate_command returns %d errno %d\n", 83362306a36Sopenharmony_ci upd_cmd, *perrno); 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci /* a status request returns immediately rather than 83762306a36Sopenharmony_ci * going into the state machine 83862306a36Sopenharmony_ci */ 83962306a36Sopenharmony_ci if (upd_cmd == I40E_NVMUPD_STATUS) { 84062306a36Sopenharmony_ci if (!cmd->data_size) { 84162306a36Sopenharmony_ci *perrno = -EFAULT; 84262306a36Sopenharmony_ci return -EINVAL; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci bytes[0] = hw->nvmupd_state; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (cmd->data_size >= 4) { 84862306a36Sopenharmony_ci bytes[1] = 0; 84962306a36Sopenharmony_ci *((u16 *)&bytes[2]) = hw->nvm_wait_opcode; 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci /* Clear error status on read */ 85362306a36Sopenharmony_ci if (hw->nvmupd_state == I40E_NVMUPD_STATE_ERROR) 85462306a36Sopenharmony_ci hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci return 0; 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci /* Clear status even it is not read and log */ 86062306a36Sopenharmony_ci if (hw->nvmupd_state == I40E_NVMUPD_STATE_ERROR) { 86162306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 86262306a36Sopenharmony_ci "Clearing I40E_NVMUPD_STATE_ERROR state without reading\n"); 86362306a36Sopenharmony_ci hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci /* Acquire lock to prevent race condition where adminq_task 86762306a36Sopenharmony_ci * can execute after i40e_nvmupd_nvm_read/write but before state 86862306a36Sopenharmony_ci * variables (nvm_wait_opcode, nvm_release_on_done) are updated. 86962306a36Sopenharmony_ci * 87062306a36Sopenharmony_ci * During NVMUpdate, it is observed that lock could be held for 87162306a36Sopenharmony_ci * ~5ms for most commands. However lock is held for ~60ms for 87262306a36Sopenharmony_ci * NVMUPD_CSUM_LCB command. 87362306a36Sopenharmony_ci */ 87462306a36Sopenharmony_ci mutex_lock(&hw->aq.arq_mutex); 87562306a36Sopenharmony_ci switch (hw->nvmupd_state) { 87662306a36Sopenharmony_ci case I40E_NVMUPD_STATE_INIT: 87762306a36Sopenharmony_ci status = i40e_nvmupd_state_init(hw, cmd, bytes, perrno); 87862306a36Sopenharmony_ci break; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci case I40E_NVMUPD_STATE_READING: 88162306a36Sopenharmony_ci status = i40e_nvmupd_state_reading(hw, cmd, bytes, perrno); 88262306a36Sopenharmony_ci break; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci case I40E_NVMUPD_STATE_WRITING: 88562306a36Sopenharmony_ci status = i40e_nvmupd_state_writing(hw, cmd, bytes, perrno); 88662306a36Sopenharmony_ci break; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci case I40E_NVMUPD_STATE_INIT_WAIT: 88962306a36Sopenharmony_ci case I40E_NVMUPD_STATE_WRITE_WAIT: 89062306a36Sopenharmony_ci /* if we need to stop waiting for an event, clear 89162306a36Sopenharmony_ci * the wait info and return before doing anything else 89262306a36Sopenharmony_ci */ 89362306a36Sopenharmony_ci if (cmd->offset == 0xffff) { 89462306a36Sopenharmony_ci i40e_nvmupd_clear_wait_state(hw); 89562306a36Sopenharmony_ci status = 0; 89662306a36Sopenharmony_ci break; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci status = -EBUSY; 90062306a36Sopenharmony_ci *perrno = -EBUSY; 90162306a36Sopenharmony_ci break; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci default: 90462306a36Sopenharmony_ci /* invalid state, should never happen */ 90562306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 90662306a36Sopenharmony_ci "NVMUPD: no such state %d\n", hw->nvmupd_state); 90762306a36Sopenharmony_ci status = -EOPNOTSUPP; 90862306a36Sopenharmony_ci *perrno = -ESRCH; 90962306a36Sopenharmony_ci break; 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci mutex_unlock(&hw->aq.arq_mutex); 91362306a36Sopenharmony_ci return status; 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci/** 91762306a36Sopenharmony_ci * i40e_nvmupd_state_init - Handle NVM update state Init 91862306a36Sopenharmony_ci * @hw: pointer to hardware structure 91962306a36Sopenharmony_ci * @cmd: pointer to nvm update command buffer 92062306a36Sopenharmony_ci * @bytes: pointer to the data buffer 92162306a36Sopenharmony_ci * @perrno: pointer to return error code 92262306a36Sopenharmony_ci * 92362306a36Sopenharmony_ci * Process legitimate commands of the Init state and conditionally set next 92462306a36Sopenharmony_ci * state. Reject all other commands. 92562306a36Sopenharmony_ci **/ 92662306a36Sopenharmony_cistatic int i40e_nvmupd_state_init(struct i40e_hw *hw, 92762306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 92862306a36Sopenharmony_ci u8 *bytes, int *perrno) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci enum i40e_nvmupd_cmd upd_cmd; 93162306a36Sopenharmony_ci int status = 0; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci switch (upd_cmd) { 93662306a36Sopenharmony_ci case I40E_NVMUPD_READ_SA: 93762306a36Sopenharmony_ci status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); 93862306a36Sopenharmony_ci if (status) { 93962306a36Sopenharmony_ci *perrno = i40e_aq_rc_to_posix(status, 94062306a36Sopenharmony_ci hw->aq.asq_last_status); 94162306a36Sopenharmony_ci } else { 94262306a36Sopenharmony_ci status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); 94362306a36Sopenharmony_ci i40e_release_nvm(hw); 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci break; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci case I40E_NVMUPD_READ_SNT: 94862306a36Sopenharmony_ci status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); 94962306a36Sopenharmony_ci if (status) { 95062306a36Sopenharmony_ci *perrno = i40e_aq_rc_to_posix(status, 95162306a36Sopenharmony_ci hw->aq.asq_last_status); 95262306a36Sopenharmony_ci } else { 95362306a36Sopenharmony_ci status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); 95462306a36Sopenharmony_ci if (status) 95562306a36Sopenharmony_ci i40e_release_nvm(hw); 95662306a36Sopenharmony_ci else 95762306a36Sopenharmony_ci hw->nvmupd_state = I40E_NVMUPD_STATE_READING; 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci break; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci case I40E_NVMUPD_WRITE_ERA: 96262306a36Sopenharmony_ci status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); 96362306a36Sopenharmony_ci if (status) { 96462306a36Sopenharmony_ci *perrno = i40e_aq_rc_to_posix(status, 96562306a36Sopenharmony_ci hw->aq.asq_last_status); 96662306a36Sopenharmony_ci } else { 96762306a36Sopenharmony_ci status = i40e_nvmupd_nvm_erase(hw, cmd, perrno); 96862306a36Sopenharmony_ci if (status) { 96962306a36Sopenharmony_ci i40e_release_nvm(hw); 97062306a36Sopenharmony_ci } else { 97162306a36Sopenharmony_ci hw->nvm_release_on_done = true; 97262306a36Sopenharmony_ci hw->nvm_wait_opcode = i40e_aqc_opc_nvm_erase; 97362306a36Sopenharmony_ci hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci break; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci case I40E_NVMUPD_WRITE_SA: 97962306a36Sopenharmony_ci status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); 98062306a36Sopenharmony_ci if (status) { 98162306a36Sopenharmony_ci *perrno = i40e_aq_rc_to_posix(status, 98262306a36Sopenharmony_ci hw->aq.asq_last_status); 98362306a36Sopenharmony_ci } else { 98462306a36Sopenharmony_ci status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); 98562306a36Sopenharmony_ci if (status) { 98662306a36Sopenharmony_ci i40e_release_nvm(hw); 98762306a36Sopenharmony_ci } else { 98862306a36Sopenharmony_ci hw->nvm_release_on_done = true; 98962306a36Sopenharmony_ci hw->nvm_wait_opcode = i40e_aqc_opc_nvm_update; 99062306a36Sopenharmony_ci hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; 99162306a36Sopenharmony_ci } 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci break; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci case I40E_NVMUPD_WRITE_SNT: 99662306a36Sopenharmony_ci status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); 99762306a36Sopenharmony_ci if (status) { 99862306a36Sopenharmony_ci *perrno = i40e_aq_rc_to_posix(status, 99962306a36Sopenharmony_ci hw->aq.asq_last_status); 100062306a36Sopenharmony_ci } else { 100162306a36Sopenharmony_ci status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); 100262306a36Sopenharmony_ci if (status) { 100362306a36Sopenharmony_ci i40e_release_nvm(hw); 100462306a36Sopenharmony_ci } else { 100562306a36Sopenharmony_ci hw->nvm_wait_opcode = i40e_aqc_opc_nvm_update; 100662306a36Sopenharmony_ci hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci break; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci case I40E_NVMUPD_CSUM_SA: 101262306a36Sopenharmony_ci status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); 101362306a36Sopenharmony_ci if (status) { 101462306a36Sopenharmony_ci *perrno = i40e_aq_rc_to_posix(status, 101562306a36Sopenharmony_ci hw->aq.asq_last_status); 101662306a36Sopenharmony_ci } else { 101762306a36Sopenharmony_ci status = i40e_update_nvm_checksum(hw); 101862306a36Sopenharmony_ci if (status) { 101962306a36Sopenharmony_ci *perrno = hw->aq.asq_last_status ? 102062306a36Sopenharmony_ci i40e_aq_rc_to_posix(status, 102162306a36Sopenharmony_ci hw->aq.asq_last_status) : 102262306a36Sopenharmony_ci -EIO; 102362306a36Sopenharmony_ci i40e_release_nvm(hw); 102462306a36Sopenharmony_ci } else { 102562306a36Sopenharmony_ci hw->nvm_release_on_done = true; 102662306a36Sopenharmony_ci hw->nvm_wait_opcode = i40e_aqc_opc_nvm_update; 102762306a36Sopenharmony_ci hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; 102862306a36Sopenharmony_ci } 102962306a36Sopenharmony_ci } 103062306a36Sopenharmony_ci break; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci case I40E_NVMUPD_EXEC_AQ: 103362306a36Sopenharmony_ci status = i40e_nvmupd_exec_aq(hw, cmd, bytes, perrno); 103462306a36Sopenharmony_ci break; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci case I40E_NVMUPD_GET_AQ_RESULT: 103762306a36Sopenharmony_ci status = i40e_nvmupd_get_aq_result(hw, cmd, bytes, perrno); 103862306a36Sopenharmony_ci break; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci case I40E_NVMUPD_GET_AQ_EVENT: 104162306a36Sopenharmony_ci status = i40e_nvmupd_get_aq_event(hw, cmd, bytes, perrno); 104262306a36Sopenharmony_ci break; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci default: 104562306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 104662306a36Sopenharmony_ci "NVMUPD: bad cmd %s in init state\n", 104762306a36Sopenharmony_ci i40e_nvm_update_state_str[upd_cmd]); 104862306a36Sopenharmony_ci status = -EIO; 104962306a36Sopenharmony_ci *perrno = -ESRCH; 105062306a36Sopenharmony_ci break; 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci return status; 105362306a36Sopenharmony_ci} 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci/** 105662306a36Sopenharmony_ci * i40e_nvmupd_state_reading - Handle NVM update state Reading 105762306a36Sopenharmony_ci * @hw: pointer to hardware structure 105862306a36Sopenharmony_ci * @cmd: pointer to nvm update command buffer 105962306a36Sopenharmony_ci * @bytes: pointer to the data buffer 106062306a36Sopenharmony_ci * @perrno: pointer to return error code 106162306a36Sopenharmony_ci * 106262306a36Sopenharmony_ci * NVM ownership is already held. Process legitimate commands and set any 106362306a36Sopenharmony_ci * change in state; reject all other commands. 106462306a36Sopenharmony_ci **/ 106562306a36Sopenharmony_cistatic int i40e_nvmupd_state_reading(struct i40e_hw *hw, 106662306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 106762306a36Sopenharmony_ci u8 *bytes, int *perrno) 106862306a36Sopenharmony_ci{ 106962306a36Sopenharmony_ci enum i40e_nvmupd_cmd upd_cmd; 107062306a36Sopenharmony_ci int status = 0; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci switch (upd_cmd) { 107562306a36Sopenharmony_ci case I40E_NVMUPD_READ_SA: 107662306a36Sopenharmony_ci case I40E_NVMUPD_READ_CON: 107762306a36Sopenharmony_ci status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); 107862306a36Sopenharmony_ci break; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci case I40E_NVMUPD_READ_LCB: 108162306a36Sopenharmony_ci status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); 108262306a36Sopenharmony_ci i40e_release_nvm(hw); 108362306a36Sopenharmony_ci hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; 108462306a36Sopenharmony_ci break; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci default: 108762306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 108862306a36Sopenharmony_ci "NVMUPD: bad cmd %s in reading state.\n", 108962306a36Sopenharmony_ci i40e_nvm_update_state_str[upd_cmd]); 109062306a36Sopenharmony_ci status = -EOPNOTSUPP; 109162306a36Sopenharmony_ci *perrno = -ESRCH; 109262306a36Sopenharmony_ci break; 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci return status; 109562306a36Sopenharmony_ci} 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci/** 109862306a36Sopenharmony_ci * i40e_nvmupd_state_writing - Handle NVM update state Writing 109962306a36Sopenharmony_ci * @hw: pointer to hardware structure 110062306a36Sopenharmony_ci * @cmd: pointer to nvm update command buffer 110162306a36Sopenharmony_ci * @bytes: pointer to the data buffer 110262306a36Sopenharmony_ci * @perrno: pointer to return error code 110362306a36Sopenharmony_ci * 110462306a36Sopenharmony_ci * NVM ownership is already held. Process legitimate commands and set any 110562306a36Sopenharmony_ci * change in state; reject all other commands 110662306a36Sopenharmony_ci **/ 110762306a36Sopenharmony_cistatic int i40e_nvmupd_state_writing(struct i40e_hw *hw, 110862306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 110962306a36Sopenharmony_ci u8 *bytes, int *perrno) 111062306a36Sopenharmony_ci{ 111162306a36Sopenharmony_ci enum i40e_nvmupd_cmd upd_cmd; 111262306a36Sopenharmony_ci bool retry_attempt = false; 111362306a36Sopenharmony_ci int status = 0; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ciretry: 111862306a36Sopenharmony_ci switch (upd_cmd) { 111962306a36Sopenharmony_ci case I40E_NVMUPD_WRITE_CON: 112062306a36Sopenharmony_ci status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); 112162306a36Sopenharmony_ci if (!status) { 112262306a36Sopenharmony_ci hw->nvm_wait_opcode = i40e_aqc_opc_nvm_update; 112362306a36Sopenharmony_ci hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci break; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci case I40E_NVMUPD_WRITE_LCB: 112862306a36Sopenharmony_ci status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); 112962306a36Sopenharmony_ci if (status) { 113062306a36Sopenharmony_ci *perrno = hw->aq.asq_last_status ? 113162306a36Sopenharmony_ci i40e_aq_rc_to_posix(status, 113262306a36Sopenharmony_ci hw->aq.asq_last_status) : 113362306a36Sopenharmony_ci -EIO; 113462306a36Sopenharmony_ci hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; 113562306a36Sopenharmony_ci } else { 113662306a36Sopenharmony_ci hw->nvm_release_on_done = true; 113762306a36Sopenharmony_ci hw->nvm_wait_opcode = i40e_aqc_opc_nvm_update; 113862306a36Sopenharmony_ci hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci break; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci case I40E_NVMUPD_CSUM_CON: 114362306a36Sopenharmony_ci /* Assumes the caller has acquired the nvm */ 114462306a36Sopenharmony_ci status = i40e_update_nvm_checksum(hw); 114562306a36Sopenharmony_ci if (status) { 114662306a36Sopenharmony_ci *perrno = hw->aq.asq_last_status ? 114762306a36Sopenharmony_ci i40e_aq_rc_to_posix(status, 114862306a36Sopenharmony_ci hw->aq.asq_last_status) : 114962306a36Sopenharmony_ci -EIO; 115062306a36Sopenharmony_ci hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; 115162306a36Sopenharmony_ci } else { 115262306a36Sopenharmony_ci hw->nvm_wait_opcode = i40e_aqc_opc_nvm_update; 115362306a36Sopenharmony_ci hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; 115462306a36Sopenharmony_ci } 115562306a36Sopenharmony_ci break; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci case I40E_NVMUPD_CSUM_LCB: 115862306a36Sopenharmony_ci /* Assumes the caller has acquired the nvm */ 115962306a36Sopenharmony_ci status = i40e_update_nvm_checksum(hw); 116062306a36Sopenharmony_ci if (status) { 116162306a36Sopenharmony_ci *perrno = hw->aq.asq_last_status ? 116262306a36Sopenharmony_ci i40e_aq_rc_to_posix(status, 116362306a36Sopenharmony_ci hw->aq.asq_last_status) : 116462306a36Sopenharmony_ci -EIO; 116562306a36Sopenharmony_ci hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; 116662306a36Sopenharmony_ci } else { 116762306a36Sopenharmony_ci hw->nvm_release_on_done = true; 116862306a36Sopenharmony_ci hw->nvm_wait_opcode = i40e_aqc_opc_nvm_update; 116962306a36Sopenharmony_ci hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci break; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci default: 117462306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 117562306a36Sopenharmony_ci "NVMUPD: bad cmd %s in writing state.\n", 117662306a36Sopenharmony_ci i40e_nvm_update_state_str[upd_cmd]); 117762306a36Sopenharmony_ci status = -EOPNOTSUPP; 117862306a36Sopenharmony_ci *perrno = -ESRCH; 117962306a36Sopenharmony_ci break; 118062306a36Sopenharmony_ci } 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci /* In some circumstances, a multi-write transaction takes longer 118362306a36Sopenharmony_ci * than the default 3 minute timeout on the write semaphore. If 118462306a36Sopenharmony_ci * the write failed with an EBUSY status, this is likely the problem, 118562306a36Sopenharmony_ci * so here we try to reacquire the semaphore then retry the write. 118662306a36Sopenharmony_ci * We only do one retry, then give up. 118762306a36Sopenharmony_ci */ 118862306a36Sopenharmony_ci if (status && (hw->aq.asq_last_status == I40E_AQ_RC_EBUSY) && 118962306a36Sopenharmony_ci !retry_attempt) { 119062306a36Sopenharmony_ci u32 old_asq_status = hw->aq.asq_last_status; 119162306a36Sopenharmony_ci int old_status = status; 119262306a36Sopenharmony_ci u32 gtime; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci gtime = rd32(hw, I40E_GLVFGEN_TIMER); 119562306a36Sopenharmony_ci if (gtime >= hw->nvm.hw_semaphore_timeout) { 119662306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_ALL, 119762306a36Sopenharmony_ci "NVMUPD: write semaphore expired (%d >= %lld), retrying\n", 119862306a36Sopenharmony_ci gtime, hw->nvm.hw_semaphore_timeout); 119962306a36Sopenharmony_ci i40e_release_nvm(hw); 120062306a36Sopenharmony_ci status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); 120162306a36Sopenharmony_ci if (status) { 120262306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_ALL, 120362306a36Sopenharmony_ci "NVMUPD: write semaphore reacquire failed aq_err = %d\n", 120462306a36Sopenharmony_ci hw->aq.asq_last_status); 120562306a36Sopenharmony_ci status = old_status; 120662306a36Sopenharmony_ci hw->aq.asq_last_status = old_asq_status; 120762306a36Sopenharmony_ci } else { 120862306a36Sopenharmony_ci retry_attempt = true; 120962306a36Sopenharmony_ci goto retry; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci } 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci return status; 121562306a36Sopenharmony_ci} 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci/** 121862306a36Sopenharmony_ci * i40e_nvmupd_clear_wait_state - clear wait state on hw 121962306a36Sopenharmony_ci * @hw: pointer to the hardware structure 122062306a36Sopenharmony_ci **/ 122162306a36Sopenharmony_civoid i40e_nvmupd_clear_wait_state(struct i40e_hw *hw) 122262306a36Sopenharmony_ci{ 122362306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 122462306a36Sopenharmony_ci "NVMUPD: clearing wait on opcode 0x%04x\n", 122562306a36Sopenharmony_ci hw->nvm_wait_opcode); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci if (hw->nvm_release_on_done) { 122862306a36Sopenharmony_ci i40e_release_nvm(hw); 122962306a36Sopenharmony_ci hw->nvm_release_on_done = false; 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci hw->nvm_wait_opcode = 0; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci if (hw->aq.arq_last_status) { 123462306a36Sopenharmony_ci hw->nvmupd_state = I40E_NVMUPD_STATE_ERROR; 123562306a36Sopenharmony_ci return; 123662306a36Sopenharmony_ci } 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci switch (hw->nvmupd_state) { 123962306a36Sopenharmony_ci case I40E_NVMUPD_STATE_INIT_WAIT: 124062306a36Sopenharmony_ci hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; 124162306a36Sopenharmony_ci break; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci case I40E_NVMUPD_STATE_WRITE_WAIT: 124462306a36Sopenharmony_ci hw->nvmupd_state = I40E_NVMUPD_STATE_WRITING; 124562306a36Sopenharmony_ci break; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci default: 124862306a36Sopenharmony_ci break; 124962306a36Sopenharmony_ci } 125062306a36Sopenharmony_ci} 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci/** 125362306a36Sopenharmony_ci * i40e_nvmupd_check_wait_event - handle NVM update operation events 125462306a36Sopenharmony_ci * @hw: pointer to the hardware structure 125562306a36Sopenharmony_ci * @opcode: the event that just happened 125662306a36Sopenharmony_ci * @desc: AdminQ descriptor 125762306a36Sopenharmony_ci **/ 125862306a36Sopenharmony_civoid i40e_nvmupd_check_wait_event(struct i40e_hw *hw, u16 opcode, 125962306a36Sopenharmony_ci struct i40e_aq_desc *desc) 126062306a36Sopenharmony_ci{ 126162306a36Sopenharmony_ci u32 aq_desc_len = sizeof(struct i40e_aq_desc); 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci if (opcode == hw->nvm_wait_opcode) { 126462306a36Sopenharmony_ci memcpy(&hw->nvm_aq_event_desc, desc, aq_desc_len); 126562306a36Sopenharmony_ci i40e_nvmupd_clear_wait_state(hw); 126662306a36Sopenharmony_ci } 126762306a36Sopenharmony_ci} 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci/** 127062306a36Sopenharmony_ci * i40e_nvmupd_validate_command - Validate given command 127162306a36Sopenharmony_ci * @hw: pointer to hardware structure 127262306a36Sopenharmony_ci * @cmd: pointer to nvm update command buffer 127362306a36Sopenharmony_ci * @perrno: pointer to return error code 127462306a36Sopenharmony_ci * 127562306a36Sopenharmony_ci * Return one of the valid command types or I40E_NVMUPD_INVALID 127662306a36Sopenharmony_ci **/ 127762306a36Sopenharmony_cistatic enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, 127862306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 127962306a36Sopenharmony_ci int *perrno) 128062306a36Sopenharmony_ci{ 128162306a36Sopenharmony_ci enum i40e_nvmupd_cmd upd_cmd; 128262306a36Sopenharmony_ci u8 module, transaction; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci /* anything that doesn't match a recognized case is an error */ 128562306a36Sopenharmony_ci upd_cmd = I40E_NVMUPD_INVALID; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci transaction = i40e_nvmupd_get_transaction(cmd->config); 128862306a36Sopenharmony_ci module = i40e_nvmupd_get_module(cmd->config); 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci /* limits on data size */ 129162306a36Sopenharmony_ci if ((cmd->data_size < 1) || 129262306a36Sopenharmony_ci (cmd->data_size > I40E_NVMUPD_MAX_DATA)) { 129362306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 129462306a36Sopenharmony_ci "i40e_nvmupd_validate_command data_size %d\n", 129562306a36Sopenharmony_ci cmd->data_size); 129662306a36Sopenharmony_ci *perrno = -EFAULT; 129762306a36Sopenharmony_ci return I40E_NVMUPD_INVALID; 129862306a36Sopenharmony_ci } 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci switch (cmd->command) { 130162306a36Sopenharmony_ci case I40E_NVM_READ: 130262306a36Sopenharmony_ci switch (transaction) { 130362306a36Sopenharmony_ci case I40E_NVM_CON: 130462306a36Sopenharmony_ci upd_cmd = I40E_NVMUPD_READ_CON; 130562306a36Sopenharmony_ci break; 130662306a36Sopenharmony_ci case I40E_NVM_SNT: 130762306a36Sopenharmony_ci upd_cmd = I40E_NVMUPD_READ_SNT; 130862306a36Sopenharmony_ci break; 130962306a36Sopenharmony_ci case I40E_NVM_LCB: 131062306a36Sopenharmony_ci upd_cmd = I40E_NVMUPD_READ_LCB; 131162306a36Sopenharmony_ci break; 131262306a36Sopenharmony_ci case I40E_NVM_SA: 131362306a36Sopenharmony_ci upd_cmd = I40E_NVMUPD_READ_SA; 131462306a36Sopenharmony_ci break; 131562306a36Sopenharmony_ci case I40E_NVM_EXEC: 131662306a36Sopenharmony_ci if (module == 0xf) 131762306a36Sopenharmony_ci upd_cmd = I40E_NVMUPD_STATUS; 131862306a36Sopenharmony_ci else if (module == 0) 131962306a36Sopenharmony_ci upd_cmd = I40E_NVMUPD_GET_AQ_RESULT; 132062306a36Sopenharmony_ci break; 132162306a36Sopenharmony_ci case I40E_NVM_AQE: 132262306a36Sopenharmony_ci upd_cmd = I40E_NVMUPD_GET_AQ_EVENT; 132362306a36Sopenharmony_ci break; 132462306a36Sopenharmony_ci } 132562306a36Sopenharmony_ci break; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci case I40E_NVM_WRITE: 132862306a36Sopenharmony_ci switch (transaction) { 132962306a36Sopenharmony_ci case I40E_NVM_CON: 133062306a36Sopenharmony_ci upd_cmd = I40E_NVMUPD_WRITE_CON; 133162306a36Sopenharmony_ci break; 133262306a36Sopenharmony_ci case I40E_NVM_SNT: 133362306a36Sopenharmony_ci upd_cmd = I40E_NVMUPD_WRITE_SNT; 133462306a36Sopenharmony_ci break; 133562306a36Sopenharmony_ci case I40E_NVM_LCB: 133662306a36Sopenharmony_ci upd_cmd = I40E_NVMUPD_WRITE_LCB; 133762306a36Sopenharmony_ci break; 133862306a36Sopenharmony_ci case I40E_NVM_SA: 133962306a36Sopenharmony_ci upd_cmd = I40E_NVMUPD_WRITE_SA; 134062306a36Sopenharmony_ci break; 134162306a36Sopenharmony_ci case I40E_NVM_ERA: 134262306a36Sopenharmony_ci upd_cmd = I40E_NVMUPD_WRITE_ERA; 134362306a36Sopenharmony_ci break; 134462306a36Sopenharmony_ci case I40E_NVM_CSUM: 134562306a36Sopenharmony_ci upd_cmd = I40E_NVMUPD_CSUM_CON; 134662306a36Sopenharmony_ci break; 134762306a36Sopenharmony_ci case (I40E_NVM_CSUM|I40E_NVM_SA): 134862306a36Sopenharmony_ci upd_cmd = I40E_NVMUPD_CSUM_SA; 134962306a36Sopenharmony_ci break; 135062306a36Sopenharmony_ci case (I40E_NVM_CSUM|I40E_NVM_LCB): 135162306a36Sopenharmony_ci upd_cmd = I40E_NVMUPD_CSUM_LCB; 135262306a36Sopenharmony_ci break; 135362306a36Sopenharmony_ci case I40E_NVM_EXEC: 135462306a36Sopenharmony_ci if (module == 0) 135562306a36Sopenharmony_ci upd_cmd = I40E_NVMUPD_EXEC_AQ; 135662306a36Sopenharmony_ci break; 135762306a36Sopenharmony_ci } 135862306a36Sopenharmony_ci break; 135962306a36Sopenharmony_ci } 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci return upd_cmd; 136262306a36Sopenharmony_ci} 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci/** 136562306a36Sopenharmony_ci * i40e_nvmupd_exec_aq - Run an AQ command 136662306a36Sopenharmony_ci * @hw: pointer to hardware structure 136762306a36Sopenharmony_ci * @cmd: pointer to nvm update command buffer 136862306a36Sopenharmony_ci * @bytes: pointer to the data buffer 136962306a36Sopenharmony_ci * @perrno: pointer to return error code 137062306a36Sopenharmony_ci * 137162306a36Sopenharmony_ci * cmd structure contains identifiers and data buffer 137262306a36Sopenharmony_ci **/ 137362306a36Sopenharmony_cistatic int i40e_nvmupd_exec_aq(struct i40e_hw *hw, 137462306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 137562306a36Sopenharmony_ci u8 *bytes, int *perrno) 137662306a36Sopenharmony_ci{ 137762306a36Sopenharmony_ci struct i40e_asq_cmd_details cmd_details; 137862306a36Sopenharmony_ci struct i40e_aq_desc *aq_desc; 137962306a36Sopenharmony_ci u32 buff_size = 0; 138062306a36Sopenharmony_ci u8 *buff = NULL; 138162306a36Sopenharmony_ci u32 aq_desc_len; 138262306a36Sopenharmony_ci u32 aq_data_len; 138362306a36Sopenharmony_ci int status; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__); 138662306a36Sopenharmony_ci if (cmd->offset == 0xffff) 138762306a36Sopenharmony_ci return 0; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci memset(&cmd_details, 0, sizeof(cmd_details)); 139062306a36Sopenharmony_ci cmd_details.wb_desc = &hw->nvm_wb_desc; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci aq_desc_len = sizeof(struct i40e_aq_desc); 139362306a36Sopenharmony_ci memset(&hw->nvm_wb_desc, 0, aq_desc_len); 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci /* get the aq descriptor */ 139662306a36Sopenharmony_ci if (cmd->data_size < aq_desc_len) { 139762306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 139862306a36Sopenharmony_ci "NVMUPD: not enough aq desc bytes for exec, size %d < %d\n", 139962306a36Sopenharmony_ci cmd->data_size, aq_desc_len); 140062306a36Sopenharmony_ci *perrno = -EINVAL; 140162306a36Sopenharmony_ci return -EINVAL; 140262306a36Sopenharmony_ci } 140362306a36Sopenharmony_ci aq_desc = (struct i40e_aq_desc *)bytes; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci /* if data buffer needed, make sure it's ready */ 140662306a36Sopenharmony_ci aq_data_len = cmd->data_size - aq_desc_len; 140762306a36Sopenharmony_ci buff_size = max_t(u32, aq_data_len, le16_to_cpu(aq_desc->datalen)); 140862306a36Sopenharmony_ci if (buff_size) { 140962306a36Sopenharmony_ci if (!hw->nvm_buff.va) { 141062306a36Sopenharmony_ci status = i40e_allocate_virt_mem(hw, &hw->nvm_buff, 141162306a36Sopenharmony_ci hw->aq.asq_buf_size); 141262306a36Sopenharmony_ci if (status) 141362306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 141462306a36Sopenharmony_ci "NVMUPD: i40e_allocate_virt_mem for exec buff failed, %d\n", 141562306a36Sopenharmony_ci status); 141662306a36Sopenharmony_ci } 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci if (hw->nvm_buff.va) { 141962306a36Sopenharmony_ci buff = hw->nvm_buff.va; 142062306a36Sopenharmony_ci memcpy(buff, &bytes[aq_desc_len], aq_data_len); 142162306a36Sopenharmony_ci } 142262306a36Sopenharmony_ci } 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci if (cmd->offset) 142562306a36Sopenharmony_ci memset(&hw->nvm_aq_event_desc, 0, aq_desc_len); 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci /* and away we go! */ 142862306a36Sopenharmony_ci status = i40e_asq_send_command(hw, aq_desc, buff, 142962306a36Sopenharmony_ci buff_size, &cmd_details); 143062306a36Sopenharmony_ci if (status) { 143162306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 143262306a36Sopenharmony_ci "%s err %pe aq_err %s\n", 143362306a36Sopenharmony_ci __func__, ERR_PTR(status), 143462306a36Sopenharmony_ci i40e_aq_str(hw, hw->aq.asq_last_status)); 143562306a36Sopenharmony_ci *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); 143662306a36Sopenharmony_ci return status; 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci /* should we wait for a followup event? */ 144062306a36Sopenharmony_ci if (cmd->offset) { 144162306a36Sopenharmony_ci hw->nvm_wait_opcode = cmd->offset; 144262306a36Sopenharmony_ci hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; 144362306a36Sopenharmony_ci } 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci return status; 144662306a36Sopenharmony_ci} 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci/** 144962306a36Sopenharmony_ci * i40e_nvmupd_get_aq_result - Get the results from the previous exec_aq 145062306a36Sopenharmony_ci * @hw: pointer to hardware structure 145162306a36Sopenharmony_ci * @cmd: pointer to nvm update command buffer 145262306a36Sopenharmony_ci * @bytes: pointer to the data buffer 145362306a36Sopenharmony_ci * @perrno: pointer to return error code 145462306a36Sopenharmony_ci * 145562306a36Sopenharmony_ci * cmd structure contains identifiers and data buffer 145662306a36Sopenharmony_ci **/ 145762306a36Sopenharmony_cistatic int i40e_nvmupd_get_aq_result(struct i40e_hw *hw, 145862306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 145962306a36Sopenharmony_ci u8 *bytes, int *perrno) 146062306a36Sopenharmony_ci{ 146162306a36Sopenharmony_ci u32 aq_total_len; 146262306a36Sopenharmony_ci u32 aq_desc_len; 146362306a36Sopenharmony_ci int remainder; 146462306a36Sopenharmony_ci u8 *buff; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__); 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci aq_desc_len = sizeof(struct i40e_aq_desc); 146962306a36Sopenharmony_ci aq_total_len = aq_desc_len + le16_to_cpu(hw->nvm_wb_desc.datalen); 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci /* check offset range */ 147262306a36Sopenharmony_ci if (cmd->offset > aq_total_len) { 147362306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, "%s: offset too big %d > %d\n", 147462306a36Sopenharmony_ci __func__, cmd->offset, aq_total_len); 147562306a36Sopenharmony_ci *perrno = -EINVAL; 147662306a36Sopenharmony_ci return -EINVAL; 147762306a36Sopenharmony_ci } 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci /* check copylength range */ 148062306a36Sopenharmony_ci if (cmd->data_size > (aq_total_len - cmd->offset)) { 148162306a36Sopenharmony_ci int new_len = aq_total_len - cmd->offset; 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, "%s: copy length %d too big, trimming to %d\n", 148462306a36Sopenharmony_ci __func__, cmd->data_size, new_len); 148562306a36Sopenharmony_ci cmd->data_size = new_len; 148662306a36Sopenharmony_ci } 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci remainder = cmd->data_size; 148962306a36Sopenharmony_ci if (cmd->offset < aq_desc_len) { 149062306a36Sopenharmony_ci u32 len = aq_desc_len - cmd->offset; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci len = min(len, cmd->data_size); 149362306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, "%s: aq_desc bytes %d to %d\n", 149462306a36Sopenharmony_ci __func__, cmd->offset, cmd->offset + len); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci buff = ((u8 *)&hw->nvm_wb_desc) + cmd->offset; 149762306a36Sopenharmony_ci memcpy(bytes, buff, len); 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci bytes += len; 150062306a36Sopenharmony_ci remainder -= len; 150162306a36Sopenharmony_ci buff = hw->nvm_buff.va; 150262306a36Sopenharmony_ci } else { 150362306a36Sopenharmony_ci buff = hw->nvm_buff.va + (cmd->offset - aq_desc_len); 150462306a36Sopenharmony_ci } 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci if (remainder > 0) { 150762306a36Sopenharmony_ci int start_byte = buff - (u8 *)hw->nvm_buff.va; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, "%s: databuf bytes %d to %d\n", 151062306a36Sopenharmony_ci __func__, start_byte, start_byte + remainder); 151162306a36Sopenharmony_ci memcpy(bytes, buff, remainder); 151262306a36Sopenharmony_ci } 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci return 0; 151562306a36Sopenharmony_ci} 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci/** 151862306a36Sopenharmony_ci * i40e_nvmupd_get_aq_event - Get the Admin Queue event from previous exec_aq 151962306a36Sopenharmony_ci * @hw: pointer to hardware structure 152062306a36Sopenharmony_ci * @cmd: pointer to nvm update command buffer 152162306a36Sopenharmony_ci * @bytes: pointer to the data buffer 152262306a36Sopenharmony_ci * @perrno: pointer to return error code 152362306a36Sopenharmony_ci * 152462306a36Sopenharmony_ci * cmd structure contains identifiers and data buffer 152562306a36Sopenharmony_ci **/ 152662306a36Sopenharmony_cistatic int i40e_nvmupd_get_aq_event(struct i40e_hw *hw, 152762306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 152862306a36Sopenharmony_ci u8 *bytes, int *perrno) 152962306a36Sopenharmony_ci{ 153062306a36Sopenharmony_ci u32 aq_total_len; 153162306a36Sopenharmony_ci u32 aq_desc_len; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__); 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci aq_desc_len = sizeof(struct i40e_aq_desc); 153662306a36Sopenharmony_ci aq_total_len = aq_desc_len + le16_to_cpu(hw->nvm_aq_event_desc.datalen); 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci /* check copylength range */ 153962306a36Sopenharmony_ci if (cmd->data_size > aq_total_len) { 154062306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 154162306a36Sopenharmony_ci "%s: copy length %d too big, trimming to %d\n", 154262306a36Sopenharmony_ci __func__, cmd->data_size, aq_total_len); 154362306a36Sopenharmony_ci cmd->data_size = aq_total_len; 154462306a36Sopenharmony_ci } 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci memcpy(bytes, &hw->nvm_aq_event_desc, cmd->data_size); 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci return 0; 154962306a36Sopenharmony_ci} 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci/** 155262306a36Sopenharmony_ci * i40e_nvmupd_nvm_read - Read NVM 155362306a36Sopenharmony_ci * @hw: pointer to hardware structure 155462306a36Sopenharmony_ci * @cmd: pointer to nvm update command buffer 155562306a36Sopenharmony_ci * @bytes: pointer to the data buffer 155662306a36Sopenharmony_ci * @perrno: pointer to return error code 155762306a36Sopenharmony_ci * 155862306a36Sopenharmony_ci * cmd structure contains identifiers and data buffer 155962306a36Sopenharmony_ci **/ 156062306a36Sopenharmony_cistatic int i40e_nvmupd_nvm_read(struct i40e_hw *hw, 156162306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 156262306a36Sopenharmony_ci u8 *bytes, int *perrno) 156362306a36Sopenharmony_ci{ 156462306a36Sopenharmony_ci struct i40e_asq_cmd_details cmd_details; 156562306a36Sopenharmony_ci u8 module, transaction; 156662306a36Sopenharmony_ci int status; 156762306a36Sopenharmony_ci bool last; 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci transaction = i40e_nvmupd_get_transaction(cmd->config); 157062306a36Sopenharmony_ci module = i40e_nvmupd_get_module(cmd->config); 157162306a36Sopenharmony_ci last = (transaction == I40E_NVM_LCB) || (transaction == I40E_NVM_SA); 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci memset(&cmd_details, 0, sizeof(cmd_details)); 157462306a36Sopenharmony_ci cmd_details.wb_desc = &hw->nvm_wb_desc; 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci status = i40e_aq_read_nvm(hw, module, cmd->offset, (u16)cmd->data_size, 157762306a36Sopenharmony_ci bytes, last, &cmd_details); 157862306a36Sopenharmony_ci if (status) { 157962306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 158062306a36Sopenharmony_ci "i40e_nvmupd_nvm_read mod 0x%x off 0x%x len 0x%x\n", 158162306a36Sopenharmony_ci module, cmd->offset, cmd->data_size); 158262306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 158362306a36Sopenharmony_ci "i40e_nvmupd_nvm_read status %d aq %d\n", 158462306a36Sopenharmony_ci status, hw->aq.asq_last_status); 158562306a36Sopenharmony_ci *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); 158662306a36Sopenharmony_ci } 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci return status; 158962306a36Sopenharmony_ci} 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci/** 159262306a36Sopenharmony_ci * i40e_nvmupd_nvm_erase - Erase an NVM module 159362306a36Sopenharmony_ci * @hw: pointer to hardware structure 159462306a36Sopenharmony_ci * @cmd: pointer to nvm update command buffer 159562306a36Sopenharmony_ci * @perrno: pointer to return error code 159662306a36Sopenharmony_ci * 159762306a36Sopenharmony_ci * module, offset, data_size and data are in cmd structure 159862306a36Sopenharmony_ci **/ 159962306a36Sopenharmony_cistatic int i40e_nvmupd_nvm_erase(struct i40e_hw *hw, 160062306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 160162306a36Sopenharmony_ci int *perrno) 160262306a36Sopenharmony_ci{ 160362306a36Sopenharmony_ci struct i40e_asq_cmd_details cmd_details; 160462306a36Sopenharmony_ci u8 module, transaction; 160562306a36Sopenharmony_ci int status = 0; 160662306a36Sopenharmony_ci bool last; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci transaction = i40e_nvmupd_get_transaction(cmd->config); 160962306a36Sopenharmony_ci module = i40e_nvmupd_get_module(cmd->config); 161062306a36Sopenharmony_ci last = (transaction & I40E_NVM_LCB); 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci memset(&cmd_details, 0, sizeof(cmd_details)); 161362306a36Sopenharmony_ci cmd_details.wb_desc = &hw->nvm_wb_desc; 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci status = i40e_aq_erase_nvm(hw, module, cmd->offset, (u16)cmd->data_size, 161662306a36Sopenharmony_ci last, &cmd_details); 161762306a36Sopenharmony_ci if (status) { 161862306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 161962306a36Sopenharmony_ci "i40e_nvmupd_nvm_erase mod 0x%x off 0x%x len 0x%x\n", 162062306a36Sopenharmony_ci module, cmd->offset, cmd->data_size); 162162306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 162262306a36Sopenharmony_ci "i40e_nvmupd_nvm_erase status %d aq %d\n", 162362306a36Sopenharmony_ci status, hw->aq.asq_last_status); 162462306a36Sopenharmony_ci *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); 162562306a36Sopenharmony_ci } 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci return status; 162862306a36Sopenharmony_ci} 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci/** 163162306a36Sopenharmony_ci * i40e_nvmupd_nvm_write - Write NVM 163262306a36Sopenharmony_ci * @hw: pointer to hardware structure 163362306a36Sopenharmony_ci * @cmd: pointer to nvm update command buffer 163462306a36Sopenharmony_ci * @bytes: pointer to the data buffer 163562306a36Sopenharmony_ci * @perrno: pointer to return error code 163662306a36Sopenharmony_ci * 163762306a36Sopenharmony_ci * module, offset, data_size and data are in cmd structure 163862306a36Sopenharmony_ci **/ 163962306a36Sopenharmony_cistatic int i40e_nvmupd_nvm_write(struct i40e_hw *hw, 164062306a36Sopenharmony_ci struct i40e_nvm_access *cmd, 164162306a36Sopenharmony_ci u8 *bytes, int *perrno) 164262306a36Sopenharmony_ci{ 164362306a36Sopenharmony_ci struct i40e_asq_cmd_details cmd_details; 164462306a36Sopenharmony_ci u8 module, transaction; 164562306a36Sopenharmony_ci u8 preservation_flags; 164662306a36Sopenharmony_ci int status = 0; 164762306a36Sopenharmony_ci bool last; 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci transaction = i40e_nvmupd_get_transaction(cmd->config); 165062306a36Sopenharmony_ci module = i40e_nvmupd_get_module(cmd->config); 165162306a36Sopenharmony_ci last = (transaction & I40E_NVM_LCB); 165262306a36Sopenharmony_ci preservation_flags = i40e_nvmupd_get_preservation_flags(cmd->config); 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci memset(&cmd_details, 0, sizeof(cmd_details)); 165562306a36Sopenharmony_ci cmd_details.wb_desc = &hw->nvm_wb_desc; 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci status = i40e_aq_update_nvm(hw, module, cmd->offset, 165862306a36Sopenharmony_ci (u16)cmd->data_size, bytes, last, 165962306a36Sopenharmony_ci preservation_flags, &cmd_details); 166062306a36Sopenharmony_ci if (status) { 166162306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 166262306a36Sopenharmony_ci "i40e_nvmupd_nvm_write mod 0x%x off 0x%x len 0x%x\n", 166362306a36Sopenharmony_ci module, cmd->offset, cmd->data_size); 166462306a36Sopenharmony_ci i40e_debug(hw, I40E_DEBUG_NVM, 166562306a36Sopenharmony_ci "i40e_nvmupd_nvm_write status %d aq %d\n", 166662306a36Sopenharmony_ci status, hw->aq.asq_last_status); 166762306a36Sopenharmony_ci *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); 166862306a36Sopenharmony_ci } 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci return status; 167162306a36Sopenharmony_ci} 1672