Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/ixl/i40e_nvm.c
Show First 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | |||||
/** | /** | ||||
* i40e_init_nvm_ops - Initialize NVM function pointers | * i40e_init_nvm_ops - Initialize NVM function pointers | ||||
* @hw: pointer to the HW structure | * @hw: pointer to the HW structure | ||||
* | * | ||||
* Setup the function pointers and the NVM info structure. Should be called | * Setup the function pointers and the NVM info structure. Should be called | ||||
* once per NVM initialization, e.g. inside the i40e_init_shared_code(). | * once per NVM initialization, e.g. inside the i40e_init_shared_code(). | ||||
* Please notice that the NVM term is used here (& in all methods covered | * Please notice that the NVM term is used here (& in all methods covered | ||||
* in this file) as an equivalent of the FLASH part mapped into the SR. | * in this file) as an equivalent of the FLASH part mapped into the SR. | ||||
* We are accessing FLASH always thru the Shadow RAM. | * We are accessing FLASH always through the Shadow RAM. | ||||
**/ | **/ | ||||
enum i40e_status_code i40e_init_nvm(struct i40e_hw *hw) | enum i40e_status_code i40e_init_nvm(struct i40e_hw *hw) | ||||
{ | { | ||||
struct i40e_nvm_info *nvm = &hw->nvm; | struct i40e_nvm_info *nvm = &hw->nvm; | ||||
enum i40e_status_code ret_code = I40E_SUCCESS; | enum i40e_status_code ret_code = I40E_SUCCESS; | ||||
u32 fla, gens; | u32 fla, gens; | ||||
u8 sr_size; | u8 sr_size; | ||||
▲ Show 20 Lines • Show All 137 Lines • ▼ Show 20 Lines | for (wait_cnt = 0; wait_cnt < I40E_SRRD_SRCTL_ATTEMPTS; wait_cnt++) { | ||||
i40e_usec_delay(5); | i40e_usec_delay(5); | ||||
} | } | ||||
if (ret_code == I40E_ERR_TIMEOUT) | if (ret_code == I40E_ERR_TIMEOUT) | ||||
i40e_debug(hw, I40E_DEBUG_NVM, "Done bit in GLNVM_SRCTL not set"); | i40e_debug(hw, I40E_DEBUG_NVM, "Done bit in GLNVM_SRCTL not set"); | ||||
return ret_code; | return ret_code; | ||||
} | } | ||||
/** | /** | ||||
* i40e_read_nvm_word - Reads Shadow RAM | * i40e_read_nvm_word - Reads nvm word and acquire lock if necessary | ||||
* @hw: pointer to the HW structure | * @hw: pointer to the HW structure | ||||
* @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) | * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) | ||||
* @data: word read from the Shadow RAM | * @data: word read from the Shadow RAM | ||||
* | * | ||||
* Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register. | * Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register. | ||||
**/ | **/ | ||||
enum i40e_status_code i40e_read_nvm_word(struct i40e_hw *hw, u16 offset, | enum i40e_status_code i40e_read_nvm_word(struct i40e_hw *hw, u16 offset, | ||||
u16 *data) | u16 *data) | ||||
{ | { | ||||
#ifdef X722_SUPPORT | enum i40e_status_code ret_code = I40E_SUCCESS; | ||||
if (hw->mac.type == I40E_MAC_X722) | |||||
return i40e_read_nvm_word_aq(hw, offset, data); | ret_code = i40e_read_nvm_word_srctl(hw, offset, data); | ||||
#endif | return ret_code; | ||||
return i40e_read_nvm_word_srctl(hw, offset, data); | |||||
} | } | ||||
/** | /** | ||||
* __i40e_read_nvm_word - Reads nvm word, assumes caller does the locking | |||||
* @hw: pointer to the HW structure | |||||
* @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) | |||||
* @data: word read from the Shadow RAM | |||||
* | |||||
* Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register. | |||||
**/ | |||||
enum i40e_status_code __i40e_read_nvm_word(struct i40e_hw *hw, | |||||
u16 offset, | |||||
u16 *data) | |||||
{ | |||||
enum i40e_status_code ret_code = I40E_SUCCESS; | |||||
ret_code = i40e_read_nvm_word_srctl(hw, offset, data); | |||||
return ret_code; | |||||
} | |||||
/** | |||||
* i40e_read_nvm_word_srctl - Reads Shadow RAM via SRCTL register | * i40e_read_nvm_word_srctl - Reads Shadow RAM via SRCTL register | ||||
* @hw: pointer to the HW structure | * @hw: pointer to the HW structure | ||||
* @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) | * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) | ||||
* @data: word read from the Shadow RAM | * @data: word read from the Shadow RAM | ||||
* | * | ||||
* Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register. | * Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register. | ||||
**/ | **/ | ||||
enum i40e_status_code i40e_read_nvm_word_srctl(struct i40e_hw *hw, u16 offset, | enum i40e_status_code i40e_read_nvm_word_srctl(struct i40e_hw *hw, u16 offset, | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | enum i40e_status_code i40e_read_nvm_word_aq(struct i40e_hw *hw, u16 offset, | ||||
ret_code = i40e_read_nvm_aq(hw, 0x0, offset, 1, data, TRUE); | ret_code = i40e_read_nvm_aq(hw, 0x0, offset, 1, data, TRUE); | ||||
*data = LE16_TO_CPU(*(__le16 *)data); | *data = LE16_TO_CPU(*(__le16 *)data); | ||||
return ret_code; | return ret_code; | ||||
} | } | ||||
/** | /** | ||||
* i40e_read_nvm_buffer - Reads Shadow RAM buffer | * __i40e_read_nvm_buffer - Reads nvm buffer, caller must acquire lock | ||||
* @hw: pointer to the HW structure | * @hw: pointer to the HW structure | ||||
* @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF). | * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF). | ||||
* @words: (in) number of words to read; (out) number of words actually read | * @words: (in) number of words to read; (out) number of words actually read | ||||
* @data: words read from the Shadow RAM | * @data: words read from the Shadow RAM | ||||
* | * | ||||
* Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd() | * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd() | ||||
* method. The buffer read is preceded by the NVM ownership take | * method. The buffer read is preceded by the NVM ownership take | ||||
* and followed by the release. | * and followed by the release. | ||||
**/ | **/ | ||||
enum i40e_status_code __i40e_read_nvm_buffer(struct i40e_hw *hw, | |||||
u16 offset, | |||||
u16 *words, u16 *data) | |||||
{ | |||||
enum i40e_status_code ret_code = I40E_SUCCESS; | |||||
ret_code = i40e_read_nvm_buffer_srctl(hw, offset, words, data); | |||||
return ret_code; | |||||
} | |||||
/** | |||||
* i40e_read_nvm_buffer - Reads Shadow RAM buffer and acuire lock if necessary | |||||
* @hw: pointer to the HW structure | |||||
* @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF). | |||||
* @words: (in) number of words to read; (out) number of words actually read | |||||
* @data: words read from the Shadow RAM | |||||
* | |||||
* Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd() | |||||
* method. The buffer read is preceded by the NVM ownership take | |||||
* and followed by the release. | |||||
**/ | |||||
enum i40e_status_code i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset, | enum i40e_status_code i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset, | ||||
u16 *words, u16 *data) | u16 *words, u16 *data) | ||||
{ | { | ||||
#ifdef X722_SUPPORT | enum i40e_status_code ret_code = I40E_SUCCESS; | ||||
if (hw->mac.type == I40E_MAC_X722) | |||||
return i40e_read_nvm_buffer_aq(hw, offset, words, data); | ret_code = i40e_read_nvm_buffer_srctl(hw, offset, words, data); | ||||
#endif | return ret_code; | ||||
return i40e_read_nvm_buffer_srctl(hw, offset, words, data); | |||||
} | } | ||||
/** | /** | ||||
* i40e_read_nvm_buffer_srctl - Reads Shadow RAM buffer via SRCTL register | * i40e_read_nvm_buffer_srctl - Reads Shadow RAM buffer via SRCTL register | ||||
* @hw: pointer to the HW structure | * @hw: pointer to the HW structure | ||||
* @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF). | * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF). | ||||
* @words: (in) number of words to read; (out) number of words actually read | * @words: (in) number of words to read; (out) number of words actually read | ||||
* @data: words read from the Shadow RAM | * @data: words read from the Shadow RAM | ||||
* | * | ||||
* Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd() | * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd() | ||||
* method. The buffer read is preceded by the NVM ownership take | * method. The buffer read is preceded by the NVM ownership take | ||||
* and followed by the release. | * and followed by the release. | ||||
**/ | **/ | ||||
enum i40e_status_code i40e_read_nvm_buffer_srctl(struct i40e_hw *hw, u16 offset, | enum i40e_status_code i40e_read_nvm_buffer_srctl(struct i40e_hw *hw, u16 offset, | ||||
u16 *words, u16 *data) | u16 *words, u16 *data) | ||||
{ | { | ||||
enum i40e_status_code ret_code = I40E_SUCCESS; | enum i40e_status_code ret_code = I40E_SUCCESS; | ||||
u16 index, word; | u16 index, word; | ||||
DEBUGFUNC("i40e_read_nvm_buffer_srctl"); | DEBUGFUNC("i40e_read_nvm_buffer_srctl"); | ||||
/* Loop thru the selected region */ | /* Loop through the selected region */ | ||||
for (word = 0; word < *words; word++) { | for (word = 0; word < *words; word++) { | ||||
index = offset + word; | index = offset + word; | ||||
ret_code = i40e_read_nvm_word_srctl(hw, index, &data[word]); | ret_code = i40e_read_nvm_word_srctl(hw, index, &data[word]); | ||||
if (ret_code != I40E_SUCCESS) | if (ret_code != I40E_SUCCESS) | ||||
break; | break; | ||||
} | } | ||||
/* Update the number of words read from the Shadow RAM */ | /* Update the number of words read from the Shadow RAM */ | ||||
▲ Show 20 Lines • Show All 155 Lines • ▼ Show 20 Lines | ret_code = i40e_aq_update_nvm(hw, module_pointer, | ||||
2 * offset, /*bytes*/ | 2 * offset, /*bytes*/ | ||||
2 * words, /*bytes*/ | 2 * words, /*bytes*/ | ||||
data, last_command, &cmd_details); | data, last_command, &cmd_details); | ||||
return ret_code; | return ret_code; | ||||
} | } | ||||
/** | /** | ||||
* i40e_write_nvm_word - Writes Shadow RAM word | * __i40e_write_nvm_word - Writes Shadow RAM word | ||||
* @hw: pointer to the HW structure | * @hw: pointer to the HW structure | ||||
* @offset: offset of the Shadow RAM word to write | * @offset: offset of the Shadow RAM word to write | ||||
* @data: word to write to the Shadow RAM | * @data: word to write to the Shadow RAM | ||||
* | * | ||||
* Writes a 16 bit word to the SR using the i40e_write_nvm_aq() method. | * Writes a 16 bit word to the SR using the i40e_write_nvm_aq() method. | ||||
* NVM ownership have to be acquired and released (on ARQ completion event | * NVM ownership have to be acquired and released (on ARQ completion event | ||||
* reception) by caller. To commit SR to NVM update checksum function | * reception) by caller. To commit SR to NVM update checksum function | ||||
* should be called. | * should be called. | ||||
**/ | **/ | ||||
enum i40e_status_code i40e_write_nvm_word(struct i40e_hw *hw, u32 offset, | enum i40e_status_code __i40e_write_nvm_word(struct i40e_hw *hw, u32 offset, | ||||
void *data) | void *data) | ||||
{ | { | ||||
DEBUGFUNC("i40e_write_nvm_word"); | DEBUGFUNC("i40e_write_nvm_word"); | ||||
*((__le16 *)data) = CPU_TO_LE16(*((u16 *)data)); | *((__le16 *)data) = CPU_TO_LE16(*((u16 *)data)); | ||||
/* Value 0x00 below means that we treat SR as a flat mem */ | /* Value 0x00 below means that we treat SR as a flat mem */ | ||||
return i40e_write_nvm_aq(hw, 0x00, offset, 1, data, FALSE); | return i40e_write_nvm_aq(hw, 0x00, offset, 1, data, FALSE); | ||||
} | } | ||||
/** | /** | ||||
* i40e_write_nvm_buffer - Writes Shadow RAM buffer | * __i40e_write_nvm_buffer - Writes Shadow RAM buffer | ||||
* @hw: pointer to the HW structure | * @hw: pointer to the HW structure | ||||
* @module_pointer: module pointer location in words from the NVM beginning | * @module_pointer: module pointer location in words from the NVM beginning | ||||
* @offset: offset of the Shadow RAM buffer to write | * @offset: offset of the Shadow RAM buffer to write | ||||
* @words: number of words to write | * @words: number of words to write | ||||
* @data: words to write to the Shadow RAM | * @data: words to write to the Shadow RAM | ||||
* | * | ||||
* Writes a 16 bit words buffer to the Shadow RAM using the admin command. | * Writes a 16 bit words buffer to the Shadow RAM using the admin command. | ||||
* NVM ownership must be acquired before calling this function and released | * NVM ownership must be acquired before calling this function and released | ||||
* on ARQ completion event reception by caller. To commit SR to NVM update | * on ARQ completion event reception by caller. To commit SR to NVM update | ||||
* checksum function should be called. | * checksum function should be called. | ||||
**/ | **/ | ||||
enum i40e_status_code i40e_write_nvm_buffer(struct i40e_hw *hw, | enum i40e_status_code __i40e_write_nvm_buffer(struct i40e_hw *hw, | ||||
u8 module_pointer, u32 offset, | u8 module_pointer, u32 offset, | ||||
u16 words, void *data) | u16 words, void *data) | ||||
{ | { | ||||
__le16 *le_word_ptr = (__le16 *)data; | __le16 *le_word_ptr = (__le16 *)data; | ||||
u16 *word_ptr = (u16 *)data; | u16 *word_ptr = (u16 *)data; | ||||
u32 i = 0; | u32 i = 0; | ||||
DEBUGFUNC("i40e_write_nvm_buffer"); | DEBUGFUNC("i40e_write_nvm_buffer"); | ||||
for (i = 0; i < words; i++) | for (i = 0; i < words; i++) | ||||
Show All 30 Lines | enum i40e_status_code i40e_calc_nvm_checksum(struct i40e_hw *hw, u16 *checksum) | ||||
ret_code = i40e_allocate_virt_mem(hw, &vmem, | ret_code = i40e_allocate_virt_mem(hw, &vmem, | ||||
I40E_SR_SECTOR_SIZE_IN_WORDS * sizeof(u16)); | I40E_SR_SECTOR_SIZE_IN_WORDS * sizeof(u16)); | ||||
if (ret_code) | if (ret_code) | ||||
goto i40e_calc_nvm_checksum_exit; | goto i40e_calc_nvm_checksum_exit; | ||||
data = (u16 *)vmem.va; | data = (u16 *)vmem.va; | ||||
/* read pointer to VPD area */ | /* read pointer to VPD area */ | ||||
ret_code = i40e_read_nvm_word(hw, I40E_SR_VPD_PTR, &vpd_module); | ret_code = __i40e_read_nvm_word(hw, I40E_SR_VPD_PTR, | ||||
&vpd_module); | |||||
if (ret_code != I40E_SUCCESS) { | if (ret_code != I40E_SUCCESS) { | ||||
ret_code = I40E_ERR_NVM_CHECKSUM; | ret_code = I40E_ERR_NVM_CHECKSUM; | ||||
goto i40e_calc_nvm_checksum_exit; | goto i40e_calc_nvm_checksum_exit; | ||||
} | } | ||||
/* read pointer to PCIe Alt Auto-load module */ | /* read pointer to PCIe Alt Auto-load module */ | ||||
ret_code = i40e_read_nvm_word(hw, I40E_SR_PCIE_ALT_AUTO_LOAD_PTR, | ret_code = __i40e_read_nvm_word(hw, | ||||
I40E_SR_PCIE_ALT_AUTO_LOAD_PTR, | |||||
&pcie_alt_module); | &pcie_alt_module); | ||||
if (ret_code != I40E_SUCCESS) { | if (ret_code != I40E_SUCCESS) { | ||||
ret_code = I40E_ERR_NVM_CHECKSUM; | ret_code = I40E_ERR_NVM_CHECKSUM; | ||||
goto i40e_calc_nvm_checksum_exit; | goto i40e_calc_nvm_checksum_exit; | ||||
} | } | ||||
/* Calculate SW checksum that covers the whole 64kB shadow RAM | /* Calculate SW checksum that covers the whole 64kB shadow RAM | ||||
* except the VPD and PCIe ALT Auto-load modules | * except the VPD and PCIe ALT Auto-load modules | ||||
*/ | */ | ||||
for (i = 0; i < hw->nvm.sr_size; i++) { | for (i = 0; i < hw->nvm.sr_size; i++) { | ||||
/* Read SR page */ | /* Read SR page */ | ||||
if ((i % I40E_SR_SECTOR_SIZE_IN_WORDS) == 0) { | if ((i % I40E_SR_SECTOR_SIZE_IN_WORDS) == 0) { | ||||
u16 words = I40E_SR_SECTOR_SIZE_IN_WORDS; | u16 words = I40E_SR_SECTOR_SIZE_IN_WORDS; | ||||
ret_code = i40e_read_nvm_buffer(hw, i, &words, data); | ret_code = __i40e_read_nvm_buffer(hw, i, &words, data); | ||||
if (ret_code != I40E_SUCCESS) { | if (ret_code != I40E_SUCCESS) { | ||||
ret_code = I40E_ERR_NVM_CHECKSUM; | ret_code = I40E_ERR_NVM_CHECKSUM; | ||||
goto i40e_calc_nvm_checksum_exit; | goto i40e_calc_nvm_checksum_exit; | ||||
} | } | ||||
} | } | ||||
/* Skip Checksum word */ | /* Skip Checksum word */ | ||||
if (i == I40E_SR_SW_CHECKSUM_WORD) | if (i == I40E_SR_SW_CHECKSUM_WORD) | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | enum i40e_status_code i40e_validate_nvm_checksum(struct i40e_hw *hw, | ||||
u16 *checksum) | u16 *checksum) | ||||
{ | { | ||||
enum i40e_status_code ret_code = I40E_SUCCESS; | enum i40e_status_code ret_code = I40E_SUCCESS; | ||||
u16 checksum_sr = 0; | u16 checksum_sr = 0; | ||||
u16 checksum_local = 0; | u16 checksum_local = 0; | ||||
DEBUGFUNC("i40e_validate_nvm_checksum"); | DEBUGFUNC("i40e_validate_nvm_checksum"); | ||||
if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) | |||||
ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); | |||||
if (!ret_code) { | |||||
ret_code = i40e_calc_nvm_checksum(hw, &checksum_local); | ret_code = i40e_calc_nvm_checksum(hw, &checksum_local); | ||||
if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) | |||||
i40e_release_nvm(hw); | |||||
if (ret_code != I40E_SUCCESS) | if (ret_code != I40E_SUCCESS) | ||||
goto i40e_validate_nvm_checksum_exit; | goto i40e_validate_nvm_checksum_exit; | ||||
} else { | |||||
goto i40e_validate_nvm_checksum_exit; | |||||
} | |||||
/* Do not use i40e_read_nvm_word() because we do not want to take | |||||
* the synchronization semaphores twice here. | |||||
*/ | |||||
i40e_read_nvm_word(hw, I40E_SR_SW_CHECKSUM_WORD, &checksum_sr); | i40e_read_nvm_word(hw, I40E_SR_SW_CHECKSUM_WORD, &checksum_sr); | ||||
/* Verify read checksum from EEPROM is the same as | /* Verify read checksum from EEPROM is the same as | ||||
* calculated checksum | * calculated checksum | ||||
*/ | */ | ||||
if (checksum_local != checksum_sr) | if (checksum_local != checksum_sr) | ||||
ret_code = I40E_ERR_NVM_CHECKSUM; | ret_code = I40E_ERR_NVM_CHECKSUM; | ||||
/* If the user cares, return the calculated checksum */ | /* If the user cares, return the calculated checksum */ | ||||
if (checksum) | if (checksum) | ||||
*checksum = checksum_local; | *checksum = checksum_local; | ||||
i40e_validate_nvm_checksum_exit: | i40e_validate_nvm_checksum_exit: | ||||
return ret_code; | return ret_code; | ||||
} | |||||
static enum i40e_status_code i40e_nvmupd_state_init(struct i40e_hw *hw, | |||||
struct i40e_nvm_access *cmd, | |||||
u8 *bytes, int *perrno); | |||||
static enum i40e_status_code i40e_nvmupd_state_reading(struct i40e_hw *hw, | |||||
struct i40e_nvm_access *cmd, | |||||
u8 *bytes, int *perrno); | |||||
static enum i40e_status_code i40e_nvmupd_state_writing(struct i40e_hw *hw, | |||||
struct i40e_nvm_access *cmd, | |||||
u8 *bytes, int *perrno); | |||||
static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, | |||||
struct i40e_nvm_access *cmd, | |||||
int *perrno); | |||||
static enum i40e_status_code i40e_nvmupd_nvm_erase(struct i40e_hw *hw, | |||||
struct i40e_nvm_access *cmd, | |||||
int *perrno); | |||||
static enum i40e_status_code i40e_nvmupd_nvm_write(struct i40e_hw *hw, | |||||
struct i40e_nvm_access *cmd, | |||||
u8 *bytes, int *perrno); | |||||
static enum i40e_status_code i40e_nvmupd_nvm_read(struct i40e_hw *hw, | |||||
struct i40e_nvm_access *cmd, | |||||
u8 *bytes, int *perrno); | |||||
static enum i40e_status_code i40e_nvmupd_exec_aq(struct i40e_hw *hw, | |||||
struct i40e_nvm_access *cmd, | |||||
u8 *bytes, int *perrno); | |||||
static enum i40e_status_code i40e_nvmupd_get_aq_result(struct i40e_hw *hw, | |||||
struct i40e_nvm_access *cmd, | |||||
u8 *bytes, int *perrno); | |||||
static INLINE u8 i40e_nvmupd_get_module(u32 val) | |||||
{ | |||||
return (u8)(val & I40E_NVM_MOD_PNT_MASK); | |||||
} | |||||
static INLINE u8 i40e_nvmupd_get_transaction(u32 val) | |||||
{ | |||||
return (u8)((val & I40E_NVM_TRANS_MASK) >> I40E_NVM_TRANS_SHIFT); | |||||
} | |||||
static const char *i40e_nvm_update_state_str[] = { | |||||
"I40E_NVMUPD_INVALID", | |||||
"I40E_NVMUPD_READ_CON", | |||||
"I40E_NVMUPD_READ_SNT", | |||||
"I40E_NVMUPD_READ_LCB", | |||||
"I40E_NVMUPD_READ_SA", | |||||
"I40E_NVMUPD_WRITE_ERA", | |||||
"I40E_NVMUPD_WRITE_CON", | |||||
"I40E_NVMUPD_WRITE_SNT", | |||||
"I40E_NVMUPD_WRITE_LCB", | |||||
"I40E_NVMUPD_WRITE_SA", | |||||
"I40E_NVMUPD_CSUM_CON", | |||||
"I40E_NVMUPD_CSUM_SA", | |||||
"I40E_NVMUPD_CSUM_LCB", | |||||
"I40E_NVMUPD_STATUS", | |||||
"I40E_NVMUPD_EXEC_AQ", | |||||
"I40E_NVMUPD_GET_AQ_RESULT", | |||||
}; | |||||
/** | |||||
* i40e_nvmupd_command - Process an NVM update command | |||||
* @hw: pointer to hardware structure | |||||
* @cmd: pointer to nvm update command | |||||
* @bytes: pointer to the data buffer | |||||
* @perrno: pointer to return error code | |||||
* | |||||
* Dispatches command depending on what update state is current | |||||
**/ | |||||
enum i40e_status_code i40e_nvmupd_command(struct i40e_hw *hw, | |||||
struct i40e_nvm_access *cmd, | |||||
u8 *bytes, int *perrno) | |||||
{ | |||||
enum i40e_status_code status; | |||||
enum i40e_nvmupd_cmd upd_cmd; | |||||
DEBUGFUNC("i40e_nvmupd_command"); | |||||
/* assume success */ | |||||
*perrno = 0; | |||||
/* early check for status command and debug msgs */ | |||||
upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); | |||||
i40e_debug(hw, I40E_DEBUG_NVM, "%s state %d nvm_release_on_hold %d cmd 0x%08x config 0x%08x offset 0x%08x data_size 0x%08x\n", | |||||
i40e_nvm_update_state_str[upd_cmd], | |||||
hw->nvmupd_state, | |||||
hw->aq.nvm_release_on_done, | |||||
cmd->command, cmd->config, cmd->offset, cmd->data_size); | |||||
if (upd_cmd == I40E_NVMUPD_INVALID) { | |||||
*perrno = -EFAULT; | |||||
i40e_debug(hw, I40E_DEBUG_NVM, | |||||
"i40e_nvmupd_validate_command returns %d errno %d\n", | |||||
upd_cmd, *perrno); | |||||
} | |||||
/* a status request returns immediately rather than | |||||
* going into the state machine | |||||
*/ | |||||
if (upd_cmd == I40E_NVMUPD_STATUS) { | |||||
bytes[0] = hw->nvmupd_state; | |||||
return I40E_SUCCESS; | |||||
} | |||||
switch (hw->nvmupd_state) { | |||||
case I40E_NVMUPD_STATE_INIT: | |||||
status = i40e_nvmupd_state_init(hw, cmd, bytes, perrno); | |||||
break; | |||||
case I40E_NVMUPD_STATE_READING: | |||||
status = i40e_nvmupd_state_reading(hw, cmd, bytes, perrno); | |||||
break; | |||||
case I40E_NVMUPD_STATE_WRITING: | |||||
status = i40e_nvmupd_state_writing(hw, cmd, bytes, perrno); | |||||
break; | |||||
case I40E_NVMUPD_STATE_INIT_WAIT: | |||||
case I40E_NVMUPD_STATE_WRITE_WAIT: | |||||
status = I40E_ERR_NOT_READY; | |||||
*perrno = -EBUSY; | |||||
break; | |||||
default: | |||||
/* invalid state, should never happen */ | |||||
i40e_debug(hw, I40E_DEBUG_NVM, | |||||
"NVMUPD: no such state %d\n", hw->nvmupd_state); | |||||
status = I40E_NOT_SUPPORTED; | |||||
*perrno = -ESRCH; | |||||
break; | |||||
} | |||||
return status; | |||||
} | |||||
/** | |||||
* i40e_nvmupd_state_init - Handle NVM update state Init | |||||
* @hw: pointer to hardware structure | |||||
* @cmd: pointer to nvm update command buffer | |||||
* @bytes: pointer to the data buffer | |||||
* @perrno: pointer to return error code | |||||
* | |||||
* Process legitimate commands of the Init state and conditionally set next | |||||
* state. Reject all other commands. | |||||
**/ | |||||
static enum i40e_status_code i40e_nvmupd_state_init(struct i40e_hw *hw, | |||||
struct i40e_nvm_access *cmd, | |||||
u8 *bytes, int *perrno) | |||||
{ | |||||
enum i40e_status_code status = I40E_SUCCESS; | |||||
enum i40e_nvmupd_cmd upd_cmd; | |||||
DEBUGFUNC("i40e_nvmupd_state_init"); | |||||
upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); | |||||
switch (upd_cmd) { | |||||
case I40E_NVMUPD_READ_SA: | |||||
status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); | |||||
if (status) { | |||||
*perrno = i40e_aq_rc_to_posix(status, | |||||
hw->aq.asq_last_status); | |||||
} else { | |||||
status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); | |||||
i40e_release_nvm(hw); | |||||
} | |||||
break; | |||||
case I40E_NVMUPD_READ_SNT: | |||||
status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); | |||||
if (status) { | |||||
*perrno = i40e_aq_rc_to_posix(status, | |||||
hw->aq.asq_last_status); | |||||
} else { | |||||
status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); | |||||
if (status) | |||||
i40e_release_nvm(hw); | |||||
else | |||||
hw->nvmupd_state = I40E_NVMUPD_STATE_READING; | |||||
} | |||||
break; | |||||
case I40E_NVMUPD_WRITE_ERA: | |||||
status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); | |||||
if (status) { | |||||
*perrno = i40e_aq_rc_to_posix(status, | |||||
hw->aq.asq_last_status); | |||||
} else { | |||||
status = i40e_nvmupd_nvm_erase(hw, cmd, perrno); | |||||
if (status) { | |||||
i40e_release_nvm(hw); | |||||
} else { | |||||
hw->aq.nvm_release_on_done = TRUE; | |||||
hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; | |||||
} | |||||
} | |||||
break; | |||||
case I40E_NVMUPD_WRITE_SA: | |||||
status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); | |||||
if (status) { | |||||
*perrno = i40e_aq_rc_to_posix(status, | |||||
hw->aq.asq_last_status); | |||||
} else { | |||||
status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); | |||||
if (status) { | |||||
i40e_release_nvm(hw); | |||||
} else { | |||||
hw->aq.nvm_release_on_done = TRUE; | |||||
hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; | |||||
} | |||||
} | |||||
break; | |||||
case I40E_NVMUPD_WRITE_SNT: | |||||
status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); | |||||
if (status) { | |||||
*perrno = i40e_aq_rc_to_posix(status, | |||||
hw->aq.asq_last_status); | |||||
} else { | |||||
status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); | |||||
if (status) | |||||
i40e_release_nvm(hw); | |||||
else | |||||
hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; | |||||
} | |||||
break; | |||||
case I40E_NVMUPD_CSUM_SA: | |||||
status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); | |||||
if (status) { | |||||
*perrno = i40e_aq_rc_to_posix(status, | |||||
hw->aq.asq_last_status); | |||||
} else { | |||||
status = i40e_update_nvm_checksum(hw); | |||||
if (status) { | |||||
*perrno = hw->aq.asq_last_status ? | |||||
i40e_aq_rc_to_posix(status, | |||||
hw->aq.asq_last_status) : | |||||
-EIO; | |||||
i40e_release_nvm(hw); | |||||
} else { | |||||
hw->aq.nvm_release_on_done = TRUE; | |||||
hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; | |||||
} | |||||
} | |||||
break; | |||||
case I40E_NVMUPD_EXEC_AQ: | |||||
status = i40e_nvmupd_exec_aq(hw, cmd, bytes, perrno); | |||||
break; | |||||
case I40E_NVMUPD_GET_AQ_RESULT: | |||||
status = i40e_nvmupd_get_aq_result(hw, cmd, bytes, perrno); | |||||
break; | |||||
default: | |||||
i40e_debug(hw, I40E_DEBUG_NVM, | |||||
"NVMUPD: bad cmd %s in init state\n", | |||||
i40e_nvm_update_state_str[upd_cmd]); | |||||
status = I40E_ERR_NVM; | |||||
*perrno = -ESRCH; | |||||
break; | |||||
} | |||||
return status; | |||||
} | |||||
/** | |||||
* i40e_nvmupd_state_reading - Handle NVM update state Reading | |||||
* @hw: pointer to hardware structure | |||||
* @cmd: pointer to nvm update command buffer | |||||
* @bytes: pointer to the data buffer | |||||
* @perrno: pointer to return error code | |||||
* | |||||
* NVM ownership is already held. Process legitimate commands and set any | |||||
* change in state; reject all other commands. | |||||
**/ | |||||
static enum i40e_status_code i40e_nvmupd_state_reading(struct i40e_hw *hw, | |||||
struct i40e_nvm_access *cmd, | |||||
u8 *bytes, int *perrno) | |||||
{ | |||||
enum i40e_status_code status = I40E_SUCCESS; | |||||
enum i40e_nvmupd_cmd upd_cmd; | |||||
DEBUGFUNC("i40e_nvmupd_state_reading"); | |||||
upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); | |||||
switch (upd_cmd) { | |||||
case I40E_NVMUPD_READ_SA: | |||||
case I40E_NVMUPD_READ_CON: | |||||
status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); | |||||
break; | |||||
case I40E_NVMUPD_READ_LCB: | |||||
status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); | |||||
i40e_release_nvm(hw); | |||||
hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; | |||||
break; | |||||
default: | |||||
i40e_debug(hw, I40E_DEBUG_NVM, | |||||
"NVMUPD: bad cmd %s in reading state.\n", | |||||
i40e_nvm_update_state_str[upd_cmd]); | |||||
status = I40E_NOT_SUPPORTED; | |||||
*perrno = -ESRCH; | |||||
break; | |||||
} | |||||
return status; | |||||
} | |||||
/** | |||||
* i40e_nvmupd_state_writing - Handle NVM update state Writing | |||||
* @hw: pointer to hardware structure | |||||
* @cmd: pointer to nvm update command buffer | |||||
* @bytes: pointer to the data buffer | |||||
* @perrno: pointer to return error code | |||||
* | |||||
* NVM ownership is already held. Process legitimate commands and set any | |||||
* change in state; reject all other commands | |||||
**/ | |||||
static enum i40e_status_code i40e_nvmupd_state_writing(struct i40e_hw *hw, | |||||
struct i40e_nvm_access *cmd, | |||||
u8 *bytes, int *perrno) | |||||
{ | |||||
enum i40e_status_code status = I40E_SUCCESS; | |||||
enum i40e_nvmupd_cmd upd_cmd; | |||||
bool retry_attempt = FALSE; | |||||
DEBUGFUNC("i40e_nvmupd_state_writing"); | |||||
upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); | |||||
retry: | |||||
switch (upd_cmd) { | |||||
case I40E_NVMUPD_WRITE_CON: | |||||
status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); | |||||
if (!status) | |||||
hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; | |||||
break; | |||||
case I40E_NVMUPD_WRITE_LCB: | |||||
status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); | |||||
if (status) { | |||||
*perrno = hw->aq.asq_last_status ? | |||||
i40e_aq_rc_to_posix(status, | |||||
hw->aq.asq_last_status) : | |||||
-EIO; | |||||
hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; | |||||
} else { | |||||
hw->aq.nvm_release_on_done = TRUE; | |||||
hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; | |||||
} | |||||
break; | |||||
case I40E_NVMUPD_CSUM_CON: | |||||
/* Assumes the caller has acquired the nvm */ | |||||
status = i40e_update_nvm_checksum(hw); | |||||
if (status) { | |||||
*perrno = hw->aq.asq_last_status ? | |||||
i40e_aq_rc_to_posix(status, | |||||
hw->aq.asq_last_status) : | |||||
-EIO; | |||||
hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; | |||||
} else { | |||||
hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; | |||||
} | |||||
break; | |||||
case I40E_NVMUPD_CSUM_LCB: | |||||
/* Assumes the caller has acquired the nvm */ | |||||
status = i40e_update_nvm_checksum(hw); | |||||
if (status) { | |||||
*perrno = hw->aq.asq_last_status ? | |||||
i40e_aq_rc_to_posix(status, | |||||
hw->aq.asq_last_status) : | |||||
-EIO; | |||||
hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; | |||||
} else { | |||||
hw->aq.nvm_release_on_done = TRUE; | |||||
hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; | |||||
} | |||||
break; | |||||
default: | |||||
i40e_debug(hw, I40E_DEBUG_NVM, | |||||
"NVMUPD: bad cmd %s in writing state.\n", | |||||
i40e_nvm_update_state_str[upd_cmd]); | |||||
status = I40E_NOT_SUPPORTED; | |||||
*perrno = -ESRCH; | |||||
break; | |||||
} | |||||
/* In some circumstances, a multi-write transaction takes longer | |||||
* than the default 3 minute timeout on the write semaphore. If | |||||
* the write failed with an EBUSY status, this is likely the problem, | |||||
* so here we try to reacquire the semaphore then retry the write. | |||||
* We only do one retry, then give up. | |||||
*/ | |||||
if (status && (hw->aq.asq_last_status == I40E_AQ_RC_EBUSY) && | |||||
!retry_attempt) { | |||||
enum i40e_status_code old_status = status; | |||||
u32 old_asq_status = hw->aq.asq_last_status; | |||||
u32 gtime; | |||||
gtime = rd32(hw, I40E_GLVFGEN_TIMER); | |||||
if (gtime >= hw->nvm.hw_semaphore_timeout) { | |||||
i40e_debug(hw, I40E_DEBUG_ALL, | |||||
"NVMUPD: write semaphore expired (%d >= %lld), retrying\n", | |||||
gtime, hw->nvm.hw_semaphore_timeout); | |||||
i40e_release_nvm(hw); | |||||
status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); | |||||
if (status) { | |||||
i40e_debug(hw, I40E_DEBUG_ALL, | |||||
"NVMUPD: write semaphore reacquire failed aq_err = %d\n", | |||||
hw->aq.asq_last_status); | |||||
status = old_status; | |||||
hw->aq.asq_last_status = old_asq_status; | |||||
} else { | |||||
retry_attempt = TRUE; | |||||
goto retry; | |||||
} | |||||
} | |||||
} | |||||
return status; | |||||
} | |||||
/** | |||||
* i40e_nvmupd_validate_command - Validate given command | |||||
* @hw: pointer to hardware structure | |||||
* @cmd: pointer to nvm update command buffer | |||||
* @perrno: pointer to return error code | |||||
* | |||||
* Return one of the valid command types or I40E_NVMUPD_INVALID | |||||
**/ | |||||
static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, | |||||
struct i40e_nvm_access *cmd, | |||||
int *perrno) | |||||
{ | |||||
enum i40e_nvmupd_cmd upd_cmd; | |||||
u8 module, transaction; | |||||
DEBUGFUNC("i40e_nvmupd_validate_command\n"); | |||||
/* anything that doesn't match a recognized case is an error */ | |||||
upd_cmd = I40E_NVMUPD_INVALID; | |||||
transaction = i40e_nvmupd_get_transaction(cmd->config); | |||||
module = i40e_nvmupd_get_module(cmd->config); | |||||
/* limits on data size */ | |||||
if ((cmd->data_size < 1) || | |||||
(cmd->data_size > I40E_NVMUPD_MAX_DATA)) { | |||||
i40e_debug(hw, I40E_DEBUG_NVM, | |||||
"i40e_nvmupd_validate_command data_size %d\n", | |||||
cmd->data_size); | |||||
*perrno = -EFAULT; | |||||
return I40E_NVMUPD_INVALID; | |||||
} | |||||
switch (cmd->command) { | |||||
case I40E_NVM_READ: | |||||
switch (transaction) { | |||||
case I40E_NVM_CON: | |||||
upd_cmd = I40E_NVMUPD_READ_CON; | |||||
break; | |||||
case I40E_NVM_SNT: | |||||
upd_cmd = I40E_NVMUPD_READ_SNT; | |||||
break; | |||||
case I40E_NVM_LCB: | |||||
upd_cmd = I40E_NVMUPD_READ_LCB; | |||||
break; | |||||
case I40E_NVM_SA: | |||||
upd_cmd = I40E_NVMUPD_READ_SA; | |||||
break; | |||||
case I40E_NVM_EXEC: | |||||
if (module == 0xf) | |||||
upd_cmd = I40E_NVMUPD_STATUS; | |||||
else if (module == 0) | |||||
upd_cmd = I40E_NVMUPD_GET_AQ_RESULT; | |||||
break; | |||||
} | |||||
break; | |||||
case I40E_NVM_WRITE: | |||||
switch (transaction) { | |||||
case I40E_NVM_CON: | |||||
upd_cmd = I40E_NVMUPD_WRITE_CON; | |||||
break; | |||||
case I40E_NVM_SNT: | |||||
upd_cmd = I40E_NVMUPD_WRITE_SNT; | |||||
break; | |||||
case I40E_NVM_LCB: | |||||
upd_cmd = I40E_NVMUPD_WRITE_LCB; | |||||
break; | |||||
case I40E_NVM_SA: | |||||
upd_cmd = I40E_NVMUPD_WRITE_SA; | |||||
break; | |||||
case I40E_NVM_ERA: | |||||
upd_cmd = I40E_NVMUPD_WRITE_ERA; | |||||
break; | |||||
case I40E_NVM_CSUM: | |||||
upd_cmd = I40E_NVMUPD_CSUM_CON; | |||||
break; | |||||
case (I40E_NVM_CSUM|I40E_NVM_SA): | |||||
upd_cmd = I40E_NVMUPD_CSUM_SA; | |||||
break; | |||||
case (I40E_NVM_CSUM|I40E_NVM_LCB): | |||||
upd_cmd = I40E_NVMUPD_CSUM_LCB; | |||||
break; | |||||
case I40E_NVM_EXEC: | |||||
if (module == 0) | |||||
upd_cmd = I40E_NVMUPD_EXEC_AQ; | |||||
break; | |||||
} | |||||
break; | |||||
} | |||||
return upd_cmd; | |||||
} | |||||
/** | |||||
* i40e_nvmupd_exec_aq - Run an AQ command | |||||
* @hw: pointer to hardware structure | |||||
* @cmd: pointer to nvm update command buffer | |||||
* @bytes: pointer to the data buffer | |||||
* @perrno: pointer to return error code | |||||
* | |||||
* cmd structure contains identifiers and data buffer | |||||
**/ | |||||
static enum i40e_status_code i40e_nvmupd_exec_aq(struct i40e_hw *hw, | |||||
struct i40e_nvm_access *cmd, | |||||
u8 *bytes, int *perrno) | |||||
{ | |||||
struct i40e_asq_cmd_details cmd_details; | |||||
enum i40e_status_code status; | |||||
struct i40e_aq_desc *aq_desc; | |||||
u32 buff_size = 0; | |||||
u8 *buff = NULL; | |||||
u32 aq_desc_len; | |||||
u32 aq_data_len; | |||||
i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__); | |||||
memset(&cmd_details, 0, sizeof(cmd_details)); | |||||
cmd_details.wb_desc = &hw->nvm_wb_desc; | |||||
aq_desc_len = sizeof(struct i40e_aq_desc); | |||||
memset(&hw->nvm_wb_desc, 0, aq_desc_len); | |||||
/* get the aq descriptor */ | |||||
if (cmd->data_size < aq_desc_len) { | |||||
i40e_debug(hw, I40E_DEBUG_NVM, | |||||
"NVMUPD: not enough aq desc bytes for exec, size %d < %d\n", | |||||
cmd->data_size, aq_desc_len); | |||||
*perrno = -EINVAL; | |||||
return I40E_ERR_PARAM; | |||||
} | |||||
aq_desc = (struct i40e_aq_desc *)bytes; | |||||
/* if data buffer needed, make sure it's ready */ | |||||
aq_data_len = cmd->data_size - aq_desc_len; | |||||
buff_size = max(aq_data_len, (u32)LE16_TO_CPU(aq_desc->datalen)); | |||||
if (buff_size) { | |||||
if (!hw->nvm_buff.va) { | |||||
status = i40e_allocate_virt_mem(hw, &hw->nvm_buff, | |||||
hw->aq.asq_buf_size); | |||||
if (status) | |||||
i40e_debug(hw, I40E_DEBUG_NVM, | |||||
"NVMUPD: i40e_allocate_virt_mem for exec buff failed, %d\n", | |||||
status); | |||||
} | |||||
if (hw->nvm_buff.va) { | |||||
buff = hw->nvm_buff.va; | |||||
memcpy(buff, &bytes[aq_desc_len], aq_data_len); | |||||
} | |||||
} | |||||
/* and away we go! */ | |||||
status = i40e_asq_send_command(hw, aq_desc, buff, | |||||
buff_size, &cmd_details); | |||||
if (status) { | |||||
i40e_debug(hw, I40E_DEBUG_NVM, | |||||
"i40e_nvmupd_exec_aq err %s aq_err %s\n", | |||||
i40e_stat_str(hw, status), | |||||
i40e_aq_str(hw, hw->aq.asq_last_status)); | |||||
*perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); | |||||
} | |||||
return status; | |||||
} | |||||
/** | |||||
* i40e_nvmupd_get_aq_result - Get the results from the previous exec_aq | |||||
* @hw: pointer to hardware structure | |||||
* @cmd: pointer to nvm update command buffer | |||||
* @bytes: pointer to the data buffer | |||||
* @perrno: pointer to return error code | |||||
* | |||||
* cmd structure contains identifiers and data buffer | |||||
**/ | |||||
static enum i40e_status_code i40e_nvmupd_get_aq_result(struct i40e_hw *hw, | |||||
struct i40e_nvm_access *cmd, | |||||
u8 *bytes, int *perrno) | |||||
{ | |||||
u32 aq_total_len; | |||||
u32 aq_desc_len; | |||||
int remainder; | |||||
u8 *buff; | |||||
i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__); | |||||
aq_desc_len = sizeof(struct i40e_aq_desc); | |||||
aq_total_len = aq_desc_len + LE16_TO_CPU(hw->nvm_wb_desc.datalen); | |||||
/* check offset range */ | |||||
if (cmd->offset > aq_total_len) { | |||||
i40e_debug(hw, I40E_DEBUG_NVM, "%s: offset too big %d > %d\n", | |||||
__func__, cmd->offset, aq_total_len); | |||||
*perrno = -EINVAL; | |||||
return I40E_ERR_PARAM; | |||||
} | |||||
/* check copylength range */ | |||||
if (cmd->data_size > (aq_total_len - cmd->offset)) { | |||||
int new_len = aq_total_len - cmd->offset; | |||||
i40e_debug(hw, I40E_DEBUG_NVM, "%s: copy length %d too big, trimming to %d\n", | |||||
__func__, cmd->data_size, new_len); | |||||
cmd->data_size = new_len; | |||||
} | |||||
remainder = cmd->data_size; | |||||
if (cmd->offset < aq_desc_len) { | |||||
u32 len = aq_desc_len - cmd->offset; | |||||
len = min(len, cmd->data_size); | |||||
i40e_debug(hw, I40E_DEBUG_NVM, "%s: aq_desc bytes %d to %d\n", | |||||
__func__, cmd->offset, cmd->offset + len); | |||||
buff = ((u8 *)&hw->nvm_wb_desc) + cmd->offset; | |||||
memcpy(bytes, buff, len); | |||||
bytes += len; | |||||
remainder -= len; | |||||
buff = hw->nvm_buff.va; | |||||
} else { | |||||
buff = (u8 *)hw->nvm_buff.va + (cmd->offset - aq_desc_len); | |||||
} | |||||
if (remainder > 0) { | |||||
int start_byte = buff - (u8 *)hw->nvm_buff.va; | |||||
i40e_debug(hw, I40E_DEBUG_NVM, "%s: databuf bytes %d to %d\n", | |||||
__func__, start_byte, start_byte + remainder); | |||||
memcpy(bytes, buff, remainder); | |||||
} | |||||
return I40E_SUCCESS; | |||||
} | |||||
/** | |||||
* i40e_nvmupd_nvm_read - Read NVM | |||||
* @hw: pointer to hardware structure | |||||
* @cmd: pointer to nvm update command buffer | |||||
* @bytes: pointer to the data buffer | |||||
* @perrno: pointer to return error code | |||||
* | |||||
* cmd structure contains identifiers and data buffer | |||||
**/ | |||||
static enum i40e_status_code i40e_nvmupd_nvm_read(struct i40e_hw *hw, | |||||
struct i40e_nvm_access *cmd, | |||||
u8 *bytes, int *perrno) | |||||
{ | |||||
struct i40e_asq_cmd_details cmd_details; | |||||
enum i40e_status_code status; | |||||
u8 module, transaction; | |||||
bool last; | |||||
transaction = i40e_nvmupd_get_transaction(cmd->config); | |||||
module = i40e_nvmupd_get_module(cmd->config); | |||||
last = (transaction == I40E_NVM_LCB) || (transaction == I40E_NVM_SA); | |||||
memset(&cmd_details, 0, sizeof(cmd_details)); | |||||
cmd_details.wb_desc = &hw->nvm_wb_desc; | |||||
status = i40e_aq_read_nvm(hw, module, cmd->offset, (u16)cmd->data_size, | |||||
bytes, last, &cmd_details); | |||||
if (status) { | |||||
i40e_debug(hw, I40E_DEBUG_NVM, | |||||
"i40e_nvmupd_nvm_read mod 0x%x off 0x%x len 0x%x\n", | |||||
module, cmd->offset, cmd->data_size); | |||||
i40e_debug(hw, I40E_DEBUG_NVM, | |||||
"i40e_nvmupd_nvm_read status %d aq %d\n", | |||||
status, hw->aq.asq_last_status); | |||||
*perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); | |||||
} | |||||
return status; | |||||
} | |||||
/** | |||||
* i40e_nvmupd_nvm_erase - Erase an NVM module | |||||
* @hw: pointer to hardware structure | |||||
* @cmd: pointer to nvm update command buffer | |||||
* @perrno: pointer to return error code | |||||
* | |||||
* module, offset, data_size and data are in cmd structure | |||||
**/ | |||||
static enum i40e_status_code i40e_nvmupd_nvm_erase(struct i40e_hw *hw, | |||||
struct i40e_nvm_access *cmd, | |||||
int *perrno) | |||||
{ | |||||
enum i40e_status_code status = I40E_SUCCESS; | |||||
struct i40e_asq_cmd_details cmd_details; | |||||
u8 module, transaction; | |||||
bool last; | |||||
transaction = i40e_nvmupd_get_transaction(cmd->config); | |||||
module = i40e_nvmupd_get_module(cmd->config); | |||||
last = (transaction & I40E_NVM_LCB); | |||||
memset(&cmd_details, 0, sizeof(cmd_details)); | |||||
cmd_details.wb_desc = &hw->nvm_wb_desc; | |||||
status = i40e_aq_erase_nvm(hw, module, cmd->offset, (u16)cmd->data_size, | |||||
last, &cmd_details); | |||||
if (status) { | |||||
i40e_debug(hw, I40E_DEBUG_NVM, | |||||
"i40e_nvmupd_nvm_erase mod 0x%x off 0x%x len 0x%x\n", | |||||
module, cmd->offset, cmd->data_size); | |||||
i40e_debug(hw, I40E_DEBUG_NVM, | |||||
"i40e_nvmupd_nvm_erase status %d aq %d\n", | |||||
status, hw->aq.asq_last_status); | |||||
*perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); | |||||
} | |||||
return status; | |||||
} | |||||
/** | |||||
* i40e_nvmupd_nvm_write - Write NVM | |||||
* @hw: pointer to hardware structure | |||||
* @cmd: pointer to nvm update command buffer | |||||
* @bytes: pointer to the data buffer | |||||
* @perrno: pointer to return error code | |||||
* | |||||
* module, offset, data_size and data are in cmd structure | |||||
**/ | |||||
static enum i40e_status_code i40e_nvmupd_nvm_write(struct i40e_hw *hw, | |||||
struct i40e_nvm_access *cmd, | |||||
u8 *bytes, int *perrno) | |||||
{ | |||||
enum i40e_status_code status = I40E_SUCCESS; | |||||
struct i40e_asq_cmd_details cmd_details; | |||||
u8 module, transaction; | |||||
bool last; | |||||
transaction = i40e_nvmupd_get_transaction(cmd->config); | |||||
module = i40e_nvmupd_get_module(cmd->config); | |||||
last = (transaction & I40E_NVM_LCB); | |||||
memset(&cmd_details, 0, sizeof(cmd_details)); | |||||
cmd_details.wb_desc = &hw->nvm_wb_desc; | |||||
status = i40e_aq_update_nvm(hw, module, cmd->offset, | |||||
(u16)cmd->data_size, bytes, last, | |||||
&cmd_details); | |||||
if (status) { | |||||
i40e_debug(hw, I40E_DEBUG_NVM, | |||||
"i40e_nvmupd_nvm_write mod 0x%x off 0x%x len 0x%x\n", | |||||
module, cmd->offset, cmd->data_size); | |||||
i40e_debug(hw, I40E_DEBUG_NVM, | |||||
"i40e_nvmupd_nvm_write status %d aq %d\n", | |||||
status, hw->aq.asq_last_status); | |||||
*perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); | |||||
} | |||||
return status; | |||||
} | } |