#include "ice_common.h"
-
/**
* ice_aq_read_nvm
* @hw: pointer to the HW struct
* @length: length of the section to be read (in bytes from the offset)
* @data: command buffer (size [bytes] = length)
* @last_command: tells if this is the last command in a series
+ * @read_shadow_ram: tell if this is a shadow RAM read
* @cd: pointer to command details structure or NULL
*
* Read the NVM using the admin queue commands (0x0701)
*/
static enum ice_status
ice_aq_read_nvm(struct ice_hw *hw, u16 module_typeid, u32 offset, u16 length,
- void *data, bool last_command, struct ice_sq_cd *cd)
+ void *data, bool last_command, bool read_shadow_ram,
+ struct ice_sq_cd *cd)
{
struct ice_aq_desc desc;
struct ice_aqc_nvm *cmd;
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_nvm_read);
+ if (!read_shadow_ram && module_typeid == ICE_AQC_NVM_START_POINT)
+ cmd->cmd_flags |= ICE_AQC_NVM_FLASH_ONLY;
+
/* If this is the last command in a series, set the proper flag. */
if (last_command)
cmd->cmd_flags |= ICE_AQC_NVM_LAST_CMD;
* So do this conversion while calling ice_aq_read_nvm.
*/
if (!status)
- status = ice_aq_read_nvm(hw, 0, 2 * offset, 2 * words, data,
- last_command, NULL);
+ status = ice_aq_read_nvm(hw, ICE_AQC_NVM_START_POINT,
+ 2 * offset, 2 * words, data,
+ last_command, true, NULL);
return status;
}
status = ice_read_sr_aq(hw, offset, 1, data, true);
if (!status)
- *data = LE16_TO_CPU(*(__le16 *)data);
+ *data = LE16_TO_CPU(*(_FORCE_ __le16 *)data);
return status;
}
-
/**
* ice_read_sr_buf_aq - Reads Shadow RAM buf via AQ
* @hw: pointer to the HW structure
} while (words_read < *words);
for (i = 0; i < *words; i++)
- data[i] = LE16_TO_CPU(((__le16 *)data)[i]);
+ data[i] = LE16_TO_CPU(((_FORCE_ __le16 *)data)[i]);
read_nvm_buf_aq_exit:
*words = words_read;
static enum ice_status
ice_acquire_nvm(struct ice_hw *hw, enum ice_aq_res_access_type access)
{
- ice_debug(hw, ICE_DBG_TRACE, "ice_acquire_nvm\n");
+ ice_debug(hw, ICE_DBG_TRACE, "%s\n", __func__);
if (hw->nvm.blank_nvm_mode)
return ICE_SUCCESS;
*/
static void ice_release_nvm(struct ice_hw *hw)
{
- ice_debug(hw, ICE_DBG_TRACE, "ice_release_nvm\n");
+ ice_debug(hw, ICE_DBG_TRACE, "%s\n", __func__);
if (hw->nvm.blank_nvm_mode)
return;
*/
enum ice_status ice_init_nvm(struct ice_hw *hw)
{
+ u16 oem_hi, oem_lo, boot_cfg_tlv, boot_cfg_tlv_len;
struct ice_nvm_info *nvm = &hw->nvm;
- u16 oem_hi, oem_lo, cfg_ptr;
u16 eetrack_lo, eetrack_hi;
- enum ice_status status = ICE_SUCCESS;
+ enum ice_status status;
u32 fla, gens_stat;
u8 sr_size;
fla = rd32(hw, GLNVM_FLA);
if (fla & GLNVM_FLA_LOCKED_M) { /* Normal programming mode */
nvm->blank_nvm_mode = false;
- } else { /* Blank programming mode */
+ } else {
+ /* Blank programming mode */
nvm->blank_nvm_mode = true;
- status = ICE_ERR_NVM_BLANK_MODE;
ice_debug(hw, ICE_DBG_NVM,
"NVM init error: unsupported blank mode.\n");
- return status;
+ return ICE_ERR_NVM_BLANK_MODE;
}
- status = ice_read_sr_word(hw, ICE_SR_NVM_DEV_STARTER_VER, &hw->nvm.ver);
+ status = ice_read_sr_word(hw, ICE_SR_NVM_DEV_STARTER_VER, &nvm->ver);
if (status) {
ice_debug(hw, ICE_DBG_INIT,
"Failed to read DEV starter version.\n");
return status;
}
- hw->nvm.eetrack = (eetrack_hi << 16) | eetrack_lo;
+ nvm->eetrack = (eetrack_hi << 16) | eetrack_lo;
+
+ /* the following devices do not have boot_cfg_tlv yet */
+ if (hw->device_id == ICE_DEV_ID_C822N_BACKPLANE ||
+ hw->device_id == ICE_DEV_ID_C822N_QSFP ||
+ hw->device_id == ICE_DEV_ID_C822N_SFP)
+ return status;
- status = ice_read_sr_word(hw, ICE_SR_BOOT_CFG_PTR, &cfg_ptr);
+ status = ice_get_pfa_module_tlv(hw, &boot_cfg_tlv, &boot_cfg_tlv_len,
+ ICE_SR_BOOT_CFG_PTR);
if (status) {
- ice_debug(hw, ICE_DBG_INIT, "Failed to read BOOT_CONFIG_PTR.\n");
+ ice_debug(hw, ICE_DBG_INIT,
+ "Failed to read Boot Configuration Block TLV.\n");
return status;
}
- status = ice_read_sr_word(hw, (cfg_ptr + ICE_NVM_OEM_VER_OFF), &oem_hi);
+ /* Boot Configuration Block must have length at least 2 words
+ * (Combo Image Version High and Combo Image Version Low)
+ */
+ if (boot_cfg_tlv_len < 2) {
+ ice_debug(hw, ICE_DBG_INIT,
+ "Invalid Boot Configuration Block TLV size.\n");
+ return ICE_ERR_INVAL_SIZE;
+ }
+
+ status = ice_read_sr_word(hw, (boot_cfg_tlv + ICE_NVM_OEM_VER_OFF),
+ &oem_hi);
if (status) {
ice_debug(hw, ICE_DBG_INIT, "Failed to read OEM_VER hi.\n");
return status;
}
- status = ice_read_sr_word(hw, (cfg_ptr + (ICE_NVM_OEM_VER_OFF + 1)),
+ status = ice_read_sr_word(hw, (boot_cfg_tlv + ICE_NVM_OEM_VER_OFF + 1),
&oem_lo);
if (status) {
ice_debug(hw, ICE_DBG_INIT, "Failed to read OEM_VER lo.\n");
return status;
}
- hw->nvm.oem_ver = ((u32)oem_hi << 16) | oem_lo;
- return status;
-}
+ nvm->oem_ver = ((u32)oem_hi << 16) | oem_lo;
+ return ICE_SUCCESS;
+}
/**
* ice_read_sr_buf - Reads Shadow RAM buf and acquire lock if necessary
return status;
}
-
/**
* ice_nvm_validate_checksum
* @hw: pointer to the HW struct
return status;
}
+
+/**
+ * ice_nvm_access_get_features - Return the NVM access features structure
+ * @cmd: NVM access command to process
+ * @data: storage for the driver NVM features
+ *
+ * Fill in the data section of the NVM access request with a copy of the NVM
+ * features structure.
+ */
+enum ice_status
+ice_nvm_access_get_features(struct ice_nvm_access_cmd *cmd,
+ union ice_nvm_access_data *data)
+{
+ /* The provided data_size must be at least as large as our NVM
+ * features structure. A larger size should not be treated as an
+ * error, to allow future extensions to to the features structure to
+ * work on older drivers.
+ */
+ if (cmd->data_size < sizeof(struct ice_nvm_features))
+ return ICE_ERR_NO_MEMORY;
+
+ /* Initialize the data buffer to zeros */
+ ice_memset(data, 0, cmd->data_size, ICE_NONDMA_MEM);
+
+ /* Fill in the features data */
+ data->drv_features.major = ICE_NVM_ACCESS_MAJOR_VER;
+ data->drv_features.minor = ICE_NVM_ACCESS_MINOR_VER;
+ data->drv_features.size = sizeof(struct ice_nvm_features);
+ data->drv_features.features[0] = ICE_NVM_FEATURES_0_REG_ACCESS;
+
+ return ICE_SUCCESS;
+}
+
+/**
+ * ice_nvm_access_get_module - Helper function to read module value
+ * @cmd: NVM access command structure
+ *
+ * Reads the module value out of the NVM access config field.
+ */
+u32 ice_nvm_access_get_module(struct ice_nvm_access_cmd *cmd)
+{
+ return ((cmd->config & ICE_NVM_CFG_MODULE_M) >> ICE_NVM_CFG_MODULE_S);
+}
+
+/**
+ * ice_nvm_access_get_flags - Helper function to read flags value
+ * @cmd: NVM access command structure
+ *
+ * Reads the flags value out of the NVM access config field.
+ */
+u32 ice_nvm_access_get_flags(struct ice_nvm_access_cmd *cmd)
+{
+ return ((cmd->config & ICE_NVM_CFG_FLAGS_M) >> ICE_NVM_CFG_FLAGS_S);
+}
+
+/**
+ * ice_nvm_access_get_adapter - Helper function to read adapter info
+ * @cmd: NVM access command structure
+ *
+ * Read the adapter info value out of the NVM access config field.
+ */
+u32 ice_nvm_access_get_adapter(struct ice_nvm_access_cmd *cmd)
+{
+ return ((cmd->config & ICE_NVM_CFG_ADAPTER_INFO_M) >>
+ ICE_NVM_CFG_ADAPTER_INFO_S);
+}
+
+/**
+ * ice_validate_nvm_rw_reg - Check than an NVM access request is valid
+ * @cmd: NVM access command structure
+ *
+ * Validates that an NVM access structure is request to read or write a valid
+ * register offset. First validates that the module and flags are correct, and
+ * then ensures that the register offset is one of the accepted registers.
+ */
+static enum ice_status
+ice_validate_nvm_rw_reg(struct ice_nvm_access_cmd *cmd)
+{
+ u32 module, flags, offset;
+ u16 i;
+
+ module = ice_nvm_access_get_module(cmd);
+ flags = ice_nvm_access_get_flags(cmd);
+ offset = cmd->offset;
+
+ /* Make sure the module and flags indicate a read/write request */
+ if (module != ICE_NVM_REG_RW_MODULE ||
+ flags != ICE_NVM_REG_RW_FLAGS ||
+ cmd->data_size != FIELD_SIZEOF(union ice_nvm_access_data, regval))
+ return ICE_ERR_PARAM;
+
+ switch (offset) {
+ case GL_HICR:
+ case GL_HICR_EN: /* Note, this register is read only */
+ case GL_FWSTS:
+ case GL_MNG_FWSM:
+ case GLGEN_CSR_DEBUG_C:
+ case GLPCI_LBARCTRL:
+ case GLNVM_GENS:
+ case GLNVM_FLA:
+ case PF_FUNC_RID:
+ return ICE_SUCCESS;
+ default:
+ break;
+ }
+
+ for (i = 0; i <= ICE_NVM_ACCESS_GL_HIDA_MAX; i++)
+ if (offset == (u32)GL_HIDA(i))
+ return ICE_SUCCESS;
+
+ for (i = 0; i <= ICE_NVM_ACCESS_GL_HIBA_MAX; i++)
+ if (offset == (u32)GL_HIBA(i))
+ return ICE_SUCCESS;
+
+ /* All other register offsets are not valid */
+ return ICE_ERR_OUT_OF_RANGE;
+}
+
+/**
+ * ice_nvm_access_read - Handle an NVM read request
+ * @hw: pointer to the HW struct
+ * @cmd: NVM access command to process
+ * @data: storage for the register value read
+ *
+ * Process an NVM access request to read a register.
+ */
+enum ice_status
+ice_nvm_access_read(struct ice_hw *hw, struct ice_nvm_access_cmd *cmd,
+ union ice_nvm_access_data *data)
+{
+ enum ice_status status;
+
+ ice_debug(hw, ICE_DBG_TRACE, "%s\n", __func__);
+
+ /* Always initialize the output data, even on failure */
+ ice_memset(data, 0, cmd->data_size, ICE_NONDMA_MEM);
+
+ /* Make sure this is a valid read/write access request */
+ status = ice_validate_nvm_rw_reg(cmd);
+ if (status)
+ return status;
+
+ ice_debug(hw, ICE_DBG_NVM, "NVM access: reading register %08x\n",
+ cmd->offset);
+
+ /* Read the register and store the contents in the data field */
+ data->regval = rd32(hw, cmd->offset);
+
+ return ICE_SUCCESS;
+}
+
+/**
+ * ice_nvm_access_write - Handle an NVM write request
+ * @hw: pointer to the HW struct
+ * @cmd: NVM access command to process
+ * @data: NVM access data to write
+ *
+ * Process an NVM access request to write a register.
+ */
+enum ice_status
+ice_nvm_access_write(struct ice_hw *hw, struct ice_nvm_access_cmd *cmd,
+ union ice_nvm_access_data *data)
+{
+ enum ice_status status;
+
+ ice_debug(hw, ICE_DBG_TRACE, "%s\n", __func__);
+
+ /* Make sure this is a valid read/write access request */
+ status = ice_validate_nvm_rw_reg(cmd);
+ if (status)
+ return status;
+
+ /* The HICR_EN register is read-only */
+ if (cmd->offset == GL_HICR_EN)
+ return ICE_ERR_OUT_OF_RANGE;
+
+ ice_debug(hw, ICE_DBG_NVM,
+ "NVM access: writing register %08x with value %08x\n",
+ cmd->offset, data->regval);
+
+ /* Write the data field to the specified register */
+ wr32(hw, cmd->offset, data->regval);
+
+ return ICE_SUCCESS;
+}
+
+/**
+ * ice_handle_nvm_access - Handle an NVM access request
+ * @hw: pointer to the HW struct
+ * @cmd: NVM access command info
+ * @data: pointer to read or return data
+ *
+ * Process an NVM access request. Read the command structure information and
+ * determine if it is valid. If not, report an error indicating the command
+ * was invalid.
+ *
+ * For valid commands, perform the necessary function, copying the data into
+ * the provided data buffer.
+ */
+enum ice_status
+ice_handle_nvm_access(struct ice_hw *hw, struct ice_nvm_access_cmd *cmd,
+ union ice_nvm_access_data *data)
+{
+ u32 module, flags, adapter_info;
+
+ ice_debug(hw, ICE_DBG_TRACE, "%s\n", __func__);
+
+ /* Extended flags are currently reserved and must be zero */
+ if ((cmd->config & ICE_NVM_CFG_EXT_FLAGS_M) != 0)
+ return ICE_ERR_PARAM;
+
+ /* Adapter info must match the HW device ID */
+ adapter_info = ice_nvm_access_get_adapter(cmd);
+ if (adapter_info != hw->device_id)
+ return ICE_ERR_PARAM;
+
+ switch (cmd->command) {
+ case ICE_NVM_CMD_READ:
+ module = ice_nvm_access_get_module(cmd);
+ flags = ice_nvm_access_get_flags(cmd);
+
+ /* Getting the driver's NVM features structure shares the same
+ * command type as reading a register. Read the config field
+ * to determine if this is a request to get features.
+ */
+ if (module == ICE_NVM_GET_FEATURES_MODULE &&
+ flags == ICE_NVM_GET_FEATURES_FLAGS &&
+ cmd->offset == 0)
+ return ice_nvm_access_get_features(cmd, data);
+ else
+ return ice_nvm_access_read(hw, cmd, data);
+ case ICE_NVM_CMD_WRITE:
+ return ice_nvm_access_write(hw, cmd, data);
+ default:
+ return ICE_ERR_PARAM;
+ }
+}