| // Copyright lowRISC contributors. | |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. | |
| // SPDX-License-Identifier: Apache-2.0 | |
| #include "sw/device/lib/dif/dif_flash_ctrl.h" | |
| #include <assert.h> | |
| #include "sw/device/lib/base/bitfield.h" | |
| #include "sw/device/lib/base/macros.h" | |
| #include "sw/device/lib/base/multibits.h" | |
| // Generated. | |
| #include "flash_ctrl_regs.h" | |
| /** | |
| * Helper function to get the offset of a data region's configuration register. | |
| * Does not check that the region actually exists, which is deferred to callers. | |
| * Since data region registers are split into two (one containing properties the | |
| * other containing address info), there are two helper functions below. | |
| * | |
| * @param region The data region's index. | |
| * @return The offset from the flash controller's base address where the region | |
| * configuration register is located. | |
| */ | |
| OT_WARN_UNUSED_RESULT | |
| static ptrdiff_t get_data_region_mp_reg_offset(uint32_t region) { | |
| return FLASH_CTRL_MP_REGION_CFG_0_REG_OFFSET + region * sizeof(uint32_t); | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| static ptrdiff_t get_data_region_reg_offset(uint32_t region) { | |
| return FLASH_CTRL_MP_REGION_0_REG_OFFSET + region * sizeof(uint32_t); | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| static ptrdiff_t get_data_region_lock_reg_offset(uint32_t region) { | |
| return FLASH_CTRL_REGION_CFG_REGWEN_0_REG_OFFSET + region * sizeof(uint32_t); | |
| } | |
| // The info region register tables have contents that depend on a particular | |
| // number of flash banks. If that number changes, the tables will need to be | |
| // updated. | |
| static_assert(FLASH_CTRL_PARAM_REG_NUM_BANKS == 2, | |
| "Please update the info region register tables."); | |
| // TODO: Can we populate the number of info partition types in the header too? | |
| // The number of different types of info regions. | |
| #ifndef FLASH_CTRL_NUM_INFO_TYPES | |
| #define FLASH_CTRL_NUM_INFO_TYPES 3 | |
| #endif | |
| // A more convenient mapping between the info regions and their configuration | |
| // register offset. | |
| static const ptrdiff_t | |
| kInfoConfigOffsets[FLASH_CTRL_NUM_INFO_TYPES] | |
| [FLASH_CTRL_PARAM_REG_NUM_BANKS] = { | |
| { | |
| FLASH_CTRL_BANK0_INFO0_PAGE_CFG_0_REG_OFFSET, | |
| FLASH_CTRL_BANK1_INFO0_PAGE_CFG_0_REG_OFFSET, | |
| }, | |
| { | |
| FLASH_CTRL_BANK0_INFO1_PAGE_CFG_REG_OFFSET, | |
| FLASH_CTRL_BANK1_INFO1_PAGE_CFG_REG_OFFSET, | |
| }, | |
| { | |
| FLASH_CTRL_BANK0_INFO2_PAGE_CFG_0_REG_OFFSET, | |
| FLASH_CTRL_BANK1_INFO2_PAGE_CFG_0_REG_OFFSET, | |
| }, | |
| }; | |
| static const ptrdiff_t | |
| kInfoLockOffsets[FLASH_CTRL_NUM_INFO_TYPES] | |
| [FLASH_CTRL_PARAM_REG_NUM_BANKS] = { | |
| { | |
| FLASH_CTRL_BANK0_INFO0_REGWEN_0_REG_OFFSET, | |
| FLASH_CTRL_BANK1_INFO0_REGWEN_0_REG_OFFSET, | |
| }, | |
| { | |
| FLASH_CTRL_BANK0_INFO1_REGWEN_REG_OFFSET, | |
| FLASH_CTRL_BANK1_INFO1_REGWEN_REG_OFFSET, | |
| }, | |
| { | |
| FLASH_CTRL_BANK0_INFO2_REGWEN_0_REG_OFFSET, | |
| FLASH_CTRL_BANK1_INFO2_REGWEN_0_REG_OFFSET, | |
| }, | |
| }; | |
| static const uint32_t kNumInfoPagesPerBank[FLASH_CTRL_NUM_INFO_TYPES] = { | |
| FLASH_CTRL_PARAM_NUM_INFOS0, | |
| FLASH_CTRL_PARAM_NUM_INFOS1, | |
| FLASH_CTRL_PARAM_NUM_INFOS2, | |
| }; | |
| /** | |
| * Helper function to get the offset of an info region's configuration register. | |
| * Does not check that the region actually exists, which is deferred to callers. | |
| * | |
| * @param region The info region's description. | |
| * @return The offset from the flash controller's base address where the region | |
| * configuration register is located. | |
| */ | |
| OT_WARN_UNUSED_RESULT | |
| static ptrdiff_t get_info_region_mp_reg_offset( | |
| dif_flash_ctrl_info_region_t region) { | |
| return kInfoConfigOffsets[region.partition_id][region.bank] + | |
| region.page * sizeof(uint32_t); | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| static ptrdiff_t get_info_region_lock_reg_offset( | |
| dif_flash_ctrl_info_region_t region) { | |
| return kInfoLockOffsets[region.partition_id][region.bank] + | |
| region.page * sizeof(uint32_t); | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_init_state(dif_flash_ctrl_state_t *handle, | |
| mmio_region_t base_addr) { | |
| if (handle == NULL) { | |
| return kDifBadArg; | |
| } | |
| dif_result_t result = dif_flash_ctrl_init(base_addr, &handle->dev); | |
| if (result != kDifOk) { | |
| return result; | |
| } | |
| handle->words_remaining = 0; | |
| handle->transaction_pending = false; | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_flash_ctrl_device_info_t dif_flash_ctrl_get_device_info(void) { | |
| const dif_flash_ctrl_device_info_t info = { | |
| .num_banks = FLASH_CTRL_PARAM_REG_NUM_BANKS, | |
| .bytes_per_word = FLASH_CTRL_PARAM_BYTES_PER_WORD, | |
| .bytes_per_page = FLASH_CTRL_PARAM_BYTES_PER_PAGE, | |
| .data_pages = FLASH_CTRL_PARAM_REG_PAGES_PER_BANK, | |
| .info0_pages = FLASH_CTRL_PARAM_NUM_INFOS0, | |
| .info1_pages = FLASH_CTRL_PARAM_NUM_INFOS1, | |
| .info2_pages = FLASH_CTRL_PARAM_NUM_INFOS2, | |
| }; | |
| return info; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_set_flash_enablement(dif_flash_ctrl_state_t *handle, | |
| dif_toggle_t enable) { | |
| enum multi_bit_bool disable_flash; | |
| if (handle == NULL) { | |
| return kDifBadArg; | |
| } | |
| // TODO: Get agreement on how multi-bit bools are used. This register has | |
| // negative logic for dif_toggle_t and treats "invalid" values differently | |
| // from the typical "true => enable" registers. | |
| switch (enable) { | |
| case kDifToggleEnabled: | |
| disable_flash = kMultiBitBool4False; | |
| break; | |
| case kDifToggleDisabled: | |
| disable_flash = kMultiBitBool4True; | |
| break; | |
| default: | |
| return kDifBadArg; | |
| } | |
| mmio_region_write32(handle->dev.base_addr, FLASH_CTRL_DIS_REG_OFFSET, | |
| disable_flash); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_get_flash_enablement( | |
| const dif_flash_ctrl_state_t *handle, dif_toggle_t *enabled_out) { | |
| if (handle == NULL || enabled_out == NULL) { | |
| return kDifBadArg; | |
| } | |
| uint32_t reg = | |
| mmio_region_read32(handle->dev.base_addr, FLASH_CTRL_DIS_REG_OFFSET); | |
| enum multi_bit_bool flash_disabled = | |
| bitfield_field32_read(reg, FLASH_CTRL_DIS_VAL_FIELD); | |
| // TODO: As above, determine if there is a convention for multi-bit bools | |
| // where "true => disable", and consider creating a common function to handle | |
| // conversion. | |
| if (flash_disabled == kMultiBitBool4False) { | |
| *enabled_out = kDifToggleEnabled; | |
| } else { | |
| *enabled_out = kDifToggleDisabled; | |
| } | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_set_exec_enablement(dif_flash_ctrl_state_t *handle, | |
| dif_toggle_t enable) { | |
| uint32_t value; | |
| if (handle == NULL) { | |
| return kDifBadArg; | |
| } | |
| switch (enable) { | |
| case kDifToggleEnabled: | |
| value = FLASH_CTRL_PARAM_EXEC_EN; | |
| break; | |
| case kDifToggleDisabled: | |
| value = 0; | |
| break; | |
| default: | |
| return kDifBadArg; | |
| } | |
| mmio_region_write32(handle->dev.base_addr, FLASH_CTRL_EXEC_REG_OFFSET, value); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_get_exec_enablement( | |
| const dif_flash_ctrl_state_t *handle, dif_toggle_t *enabled_out) { | |
| if (handle == NULL || enabled_out == NULL) { | |
| return kDifBadArg; | |
| } | |
| uint32_t reg = | |
| mmio_region_read32(handle->dev.base_addr, FLASH_CTRL_EXEC_REG_OFFSET); | |
| if (reg == FLASH_CTRL_PARAM_EXEC_EN) { | |
| *enabled_out = kDifToggleEnabled; | |
| } else { | |
| *enabled_out = kDifToggleDisabled; | |
| } | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_start_controller_init( | |
| dif_flash_ctrl_state_t *handle) { | |
| if (handle == NULL) { | |
| return kDifBadArg; | |
| } | |
| const uint32_t reg = | |
| mmio_region_read32(handle->dev.base_addr, FLASH_CTRL_INIT_REG_OFFSET); | |
| if (bitfield_bit32_read(reg, FLASH_CTRL_INIT_VAL_BIT)) { | |
| // Controller initialization may only be requested once. | |
| return kDifError; | |
| } | |
| uint32_t value = bitfield_bit32_write(0, FLASH_CTRL_INIT_VAL_BIT, true); | |
| mmio_region_write32(handle->dev.base_addr, FLASH_CTRL_INIT_REG_OFFSET, value); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_get_status(const dif_flash_ctrl_state_t *handle, | |
| dif_flash_ctrl_status_t *status_out) { | |
| if (handle == NULL || status_out == NULL) { | |
| return kDifBadArg; | |
| } | |
| uint32_t reg = | |
| mmio_region_read32(handle->dev.base_addr, FLASH_CTRL_STATUS_REG_OFFSET); | |
| dif_flash_ctrl_status_t status = { | |
| .read_fifo_full = bitfield_bit32_read(reg, FLASH_CTRL_STATUS_RD_FULL_BIT), | |
| .read_fifo_empty = | |
| bitfield_bit32_read(reg, FLASH_CTRL_STATUS_RD_EMPTY_BIT), | |
| .prog_fifo_full = | |
| bitfield_bit32_read(reg, FLASH_CTRL_STATUS_PROG_FULL_BIT), | |
| .prog_fifo_empty = | |
| bitfield_bit32_read(reg, FLASH_CTRL_STATUS_PROG_EMPTY_BIT), | |
| .controller_init_wip = | |
| bitfield_bit32_read(reg, FLASH_CTRL_STATUS_INIT_WIP_BIT), | |
| }; | |
| *status_out = status; | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_get_allowed_prog_types( | |
| const dif_flash_ctrl_state_t *handle, | |
| dif_flash_ctrl_prog_capabilities_t *allowed_types_out) { | |
| if (handle == NULL || allowed_types_out == NULL) { | |
| return kDifBadArg; | |
| } | |
| uint32_t reg = mmio_region_read32(handle->dev.base_addr, | |
| FLASH_CTRL_PROG_TYPE_EN_REG_OFFSET); | |
| dif_flash_ctrl_prog_capabilities_t allowed_types = { | |
| .normal_prog_type = | |
| bitfield_bit32_read(reg, FLASH_CTRL_PROG_TYPE_EN_NORMAL_BIT), | |
| .repair_prog_type = | |
| bitfield_bit32_read(reg, FLASH_CTRL_PROG_TYPE_EN_REPAIR_BIT), | |
| }; | |
| *allowed_types_out = allowed_types; | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_disallow_prog_types( | |
| dif_flash_ctrl_state_t *handle, | |
| dif_flash_ctrl_prog_capabilities_t types_to_disallow) { | |
| if (handle == NULL) { | |
| return kDifBadArg; | |
| } | |
| uint32_t ctrl_regwen = mmio_region_read32(handle->dev.base_addr, | |
| FLASH_CTRL_CTRL_REGWEN_REG_OFFSET); | |
| if (!bitfield_bit32_read(ctrl_regwen, FLASH_CTRL_CTRL_REGWEN_EN_BIT)) { | |
| return kDifUnavailable; | |
| } | |
| uint32_t reg = 0; | |
| reg = bitfield_bit32_write(reg, FLASH_CTRL_PROG_TYPE_EN_NORMAL_BIT, | |
| !types_to_disallow.normal_prog_type); | |
| reg = bitfield_bit32_write(reg, FLASH_CTRL_PROG_TYPE_EN_REPAIR_BIT, | |
| !types_to_disallow.repair_prog_type); | |
| mmio_region_write32(handle->dev.base_addr, FLASH_CTRL_PROG_TYPE_EN_REG_OFFSET, | |
| reg); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_start_unsafe( | |
| dif_flash_ctrl_state_t *handle, dif_flash_ctrl_transaction_t transaction) { | |
| if (handle == NULL) { | |
| return kDifBadArg; | |
| } | |
| uint32_t ctrl_regwen = mmio_region_read32(handle->dev.base_addr, | |
| FLASH_CTRL_CTRL_REGWEN_REG_OFFSET); | |
| if (!bitfield_bit32_read(ctrl_regwen, FLASH_CTRL_CTRL_REGWEN_EN_BIT)) { | |
| return kDifUnavailable; | |
| } | |
| uint32_t control_reg = bitfield_field32_write(0, FLASH_CTRL_CONTROL_NUM_FIELD, | |
| transaction.word_count - 1); | |
| switch (transaction.op) { | |
| case kDifFlashCtrlOpRead: | |
| control_reg = | |
| bitfield_field32_write(control_reg, FLASH_CTRL_CONTROL_OP_FIELD, | |
| FLASH_CTRL_CONTROL_OP_VALUE_READ); | |
| break; | |
| case kDifFlashCtrlOpProgram: | |
| control_reg = | |
| bitfield_field32_write(control_reg, FLASH_CTRL_CONTROL_OP_FIELD, | |
| FLASH_CTRL_CONTROL_OP_VALUE_PROG); | |
| control_reg = bitfield_bit32_write( | |
| control_reg, FLASH_CTRL_CONTROL_PROG_SEL_BIT, false); | |
| break; | |
| case kDifFlashCtrlOpProgramRepair: | |
| control_reg = | |
| bitfield_field32_write(control_reg, FLASH_CTRL_CONTROL_OP_FIELD, | |
| FLASH_CTRL_CONTROL_OP_VALUE_PROG); | |
| control_reg = bitfield_bit32_write(control_reg, | |
| FLASH_CTRL_CONTROL_PROG_SEL_BIT, true); | |
| break; | |
| case kDifFlashCtrlOpPageErase: | |
| control_reg = | |
| bitfield_field32_write(control_reg, FLASH_CTRL_CONTROL_OP_FIELD, | |
| FLASH_CTRL_CONTROL_OP_VALUE_ERASE); | |
| control_reg = bitfield_bit32_write( | |
| control_reg, FLASH_CTRL_CONTROL_ERASE_SEL_BIT, false); | |
| break; | |
| case kDifFlashCtrlOpBankErase: | |
| control_reg = | |
| bitfield_field32_write(control_reg, FLASH_CTRL_CONTROL_OP_FIELD, | |
| FLASH_CTRL_CONTROL_OP_VALUE_ERASE); | |
| control_reg = bitfield_bit32_write( | |
| control_reg, FLASH_CTRL_CONTROL_ERASE_SEL_BIT, true); | |
| break; | |
| default: | |
| return kDifBadArg; | |
| } | |
| switch (transaction.partition_type) { | |
| case kDifFlashCtrlPartitionTypeData: | |
| control_reg = bitfield_bit32_write( | |
| control_reg, FLASH_CTRL_CONTROL_PARTITION_SEL_BIT, false); | |
| break; | |
| case kDifFlashCtrlPartitionTypeInfo: | |
| control_reg = | |
| bitfield_field32_write(control_reg, FLASH_CTRL_CONTROL_INFO_SEL_FIELD, | |
| transaction.partition_id); | |
| control_reg = bitfield_bit32_write( | |
| control_reg, FLASH_CTRL_CONTROL_PARTITION_SEL_BIT, true); | |
| break; | |
| default: | |
| return kDifBadArg; | |
| } | |
| mmio_region_write32(handle->dev.base_addr, FLASH_CTRL_CONTROL_REG_OFFSET, | |
| control_reg); | |
| mmio_region_write32(handle->dev.base_addr, FLASH_CTRL_ADDR_REG_OFFSET, | |
| transaction.byte_address); | |
| mmio_region_write32(handle->dev.base_addr, FLASH_CTRL_CONTROL_REG_OFFSET, | |
| control_reg | (1u << FLASH_CTRL_CONTROL_START_BIT)); | |
| if (transaction.op == kDifFlashCtrlOpPageErase || | |
| transaction.op == kDifFlashCtrlOpBankErase) { | |
| // Erase operations don't use the FIFO | |
| handle->words_remaining = 0; | |
| } else { | |
| handle->words_remaining = transaction.word_count; | |
| } | |
| handle->transaction_pending = true; | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_start(dif_flash_ctrl_state_t *handle, | |
| dif_flash_ctrl_transaction_t transaction) { | |
| if (handle == NULL) { | |
| return kDifBadArg; | |
| } | |
| if (transaction.partition_type == kDifFlashCtrlPartitionTypeInfo && | |
| transaction.partition_id >= FLASH_CTRL_NUM_INFO_TYPES) { | |
| return kDifBadArg; | |
| } | |
| const uint32_t max_word_count = FLASH_CTRL_CONTROL_NUM_MASK; | |
| if ((transaction.op != kDifFlashCtrlOpPageErase) && | |
| (transaction.op != kDifFlashCtrlOpBankErase)) { | |
| if (transaction.word_count - 1 > max_word_count || | |
| transaction.word_count == 0) { | |
| return kDifBadArg; | |
| } | |
| } | |
| if (handle->transaction_pending) { | |
| return kDifUnavailable; | |
| } | |
| // Disallow starting a new transaction if the FIFOs haven't been emptied yet. | |
| dif_flash_ctrl_status_t status; | |
| dif_result_t result = dif_flash_ctrl_get_status(handle, &status); | |
| if (result != kDifOk) { | |
| return result; | |
| } | |
| if (!status.read_fifo_empty || !status.prog_fifo_empty) { | |
| return kDifIpFifoFull; | |
| } | |
| return dif_flash_ctrl_start_unsafe(handle, transaction); | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_suspend_erase(dif_flash_ctrl_state_t *handle) { | |
| if (handle == NULL) { | |
| return kDifBadArg; | |
| } | |
| uint32_t reg = | |
| bitfield_bit32_write(0, FLASH_CTRL_ERASE_SUSPEND_REQ_BIT, true); | |
| mmio_region_write32(handle->dev.base_addr, | |
| FLASH_CTRL_ERASE_SUSPEND_REG_OFFSET, reg); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_get_erase_suspend_status( | |
| dif_flash_ctrl_state_t *handle, bool *request_pending_out) { | |
| if (handle == NULL || request_pending_out == NULL) { | |
| return kDifBadArg; | |
| } | |
| uint32_t reg = mmio_region_read32(handle->dev.base_addr, | |
| FLASH_CTRL_ERASE_SUSPEND_REG_OFFSET); | |
| *request_pending_out = | |
| bitfield_bit32_read(reg, FLASH_CTRL_ERASE_SUSPEND_REQ_BIT); | |
| return kDifOk; | |
| } | |
| // TODO(awill): Function to report the maximum FIFO size? | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_prog_fifo_push_unsafe( | |
| dif_flash_ctrl_state_t *handle, uint32_t word_count, const uint32_t *data) { | |
| if (handle == NULL || data == NULL) { | |
| return kDifBadArg; | |
| } | |
| uint32_t written = 0; | |
| while (written < word_count) { | |
| mmio_region_write32(handle->dev.base_addr, FLASH_CTRL_PROG_FIFO_REG_OFFSET, | |
| data[written]); | |
| ++written; | |
| } | |
| handle->words_remaining -= written; | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_prog_fifo_push(dif_flash_ctrl_state_t *handle, | |
| uint32_t word_count, | |
| const uint32_t *data) { | |
| if (handle == NULL || data == NULL) { | |
| return kDifBadArg; | |
| } | |
| if (!handle->transaction_pending) { | |
| return kDifError; | |
| } | |
| if (handle->words_remaining < word_count) { | |
| return kDifBadArg; | |
| } | |
| const uint32_t control_reg = | |
| mmio_region_read32(handle->dev.base_addr, FLASH_CTRL_CONTROL_REG_OFFSET); | |
| const uint32_t op = | |
| bitfield_field32_read(control_reg, FLASH_CTRL_CONTROL_OP_FIELD); | |
| if (op != FLASH_CTRL_CONTROL_OP_VALUE_PROG) { | |
| return kDifError; | |
| } | |
| return dif_flash_ctrl_prog_fifo_push_unsafe(handle, word_count, data); | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_read_fifo_pop_unsafe(dif_flash_ctrl_state_t *handle, | |
| uint32_t word_count, | |
| uint32_t *data) { | |
| if (handle == NULL || data == NULL) { | |
| return kDifBadArg; | |
| } | |
| uint32_t read = 0; | |
| while (read < word_count) { | |
| data[read] = mmio_region_read32(handle->dev.base_addr, | |
| FLASH_CTRL_RD_FIFO_REG_OFFSET); | |
| ++read; | |
| } | |
| handle->words_remaining -= read; | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_read_fifo_pop(dif_flash_ctrl_state_t *handle, | |
| uint32_t word_count, uint32_t *data) { | |
| if (handle == NULL || data == NULL) { | |
| return kDifBadArg; | |
| } | |
| if (!handle->transaction_pending) { | |
| return kDifError; | |
| } | |
| if (handle->words_remaining < word_count) { | |
| return kDifBadArg; | |
| } | |
| const uint32_t control_reg = | |
| mmio_region_read32(handle->dev.base_addr, FLASH_CTRL_CONTROL_REG_OFFSET); | |
| const uint32_t op = | |
| bitfield_field32_read(control_reg, FLASH_CTRL_CONTROL_OP_FIELD); | |
| if (op != FLASH_CTRL_CONTROL_OP_VALUE_READ) { | |
| return kDifError; | |
| } | |
| return dif_flash_ctrl_read_fifo_pop_unsafe(handle, word_count, data); | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_get_error_codes( | |
| const dif_flash_ctrl_state_t *handle, | |
| dif_flash_ctrl_error_t *error_code_out) { | |
| if (handle == NULL || error_code_out == NULL) { | |
| return kDifBadArg; | |
| } | |
| const uint32_t code_reg = | |
| mmio_region_read32(handle->dev.base_addr, FLASH_CTRL_ERR_CODE_REG_OFFSET); | |
| dif_flash_ctrl_error_codes_t codes = { | |
| .memory_properties_error = | |
| bitfield_bit32_read(code_reg, FLASH_CTRL_ERR_CODE_MP_ERR_BIT), | |
| .read_error = | |
| bitfield_bit32_read(code_reg, FLASH_CTRL_ERR_CODE_RD_ERR_BIT), | |
| .prog_window_error = | |
| bitfield_bit32_read(code_reg, FLASH_CTRL_ERR_CODE_PROG_WIN_ERR_BIT), | |
| .prog_type_error = | |
| bitfield_bit32_read(code_reg, FLASH_CTRL_ERR_CODE_PROG_TYPE_ERR_BIT), | |
| .shadow_register_error = | |
| bitfield_bit32_read(code_reg, FLASH_CTRL_ERR_CODE_UPDATE_ERR_BIT), | |
| }; | |
| const uint32_t address_reg = | |
| mmio_region_read32(handle->dev.base_addr, FLASH_CTRL_ERR_ADDR_REG_OFFSET); | |
| dif_flash_ctrl_error_t error_code = { | |
| .address = address_reg, | |
| .codes = codes, | |
| }; | |
| *error_code_out = error_code; | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_clear_error_codes( | |
| dif_flash_ctrl_state_t *handle, dif_flash_ctrl_error_codes_t codes) { | |
| if (handle == NULL) { | |
| return kDifBadArg; | |
| } | |
| uint32_t code_reg = 0; | |
| code_reg = bitfield_bit32_write(code_reg, FLASH_CTRL_ERR_CODE_MP_ERR_BIT, | |
| codes.memory_properties_error); | |
| code_reg = bitfield_bit32_write(code_reg, FLASH_CTRL_ERR_CODE_RD_ERR_BIT, | |
| codes.read_error); | |
| code_reg = bitfield_bit32_write( | |
| code_reg, FLASH_CTRL_ERR_CODE_PROG_WIN_ERR_BIT, codes.prog_window_error); | |
| code_reg = bitfield_bit32_write( | |
| code_reg, FLASH_CTRL_ERR_CODE_PROG_TYPE_ERR_BIT, codes.prog_type_error); | |
| code_reg = bitfield_bit32_write(code_reg, FLASH_CTRL_ERR_CODE_UPDATE_ERR_BIT, | |
| codes.shadow_register_error); | |
| mmio_region_write32(handle->dev.base_addr, FLASH_CTRL_ERR_CODE_REG_OFFSET, | |
| code_reg); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_end(dif_flash_ctrl_state_t *handle, | |
| dif_flash_ctrl_output_t *out) { | |
| if (handle == NULL || out == NULL) { | |
| return kDifBadArg; | |
| } | |
| if (!handle->transaction_pending) { | |
| return kDifError; | |
| } | |
| uint32_t status_reg = mmio_region_read32(handle->dev.base_addr, | |
| FLASH_CTRL_OP_STATUS_REG_OFFSET); | |
| if (!bitfield_bit32_read(status_reg, FLASH_CTRL_OP_STATUS_DONE_BIT)) { | |
| return kDifUnavailable; | |
| } | |
| if (handle->words_remaining != 0) { | |
| return kDifIpFifoFull; | |
| } | |
| out->operation_done = | |
| bitfield_bit32_read(status_reg, FLASH_CTRL_OP_STATUS_DONE_BIT); | |
| out->operation_error = | |
| bitfield_bit32_read(status_reg, FLASH_CTRL_OP_STATUS_ERR_BIT); | |
| // Clear the operation status | |
| mmio_region_write32(handle->dev.base_addr, FLASH_CTRL_OP_STATUS_REG_OFFSET, | |
| 0); | |
| handle->transaction_pending = false; | |
| return dif_flash_ctrl_get_error_codes(handle, &out->error_code); | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_set_data_region_enablement( | |
| dif_flash_ctrl_state_t *handle, uint32_t region, dif_toggle_t enable) { | |
| if (handle == NULL || region >= FLASH_CTRL_PARAM_NUM_REGIONS) { | |
| return kDifBadArg; | |
| } | |
| bool locked; | |
| DIF_RETURN_IF_ERROR( | |
| dif_flash_ctrl_data_region_is_locked(handle, region, &locked)); | |
| if (locked) { | |
| return kDifLocked; | |
| } | |
| ptrdiff_t mp_reg_offset = get_data_region_mp_reg_offset(region); | |
| uint32_t mp_reg = mmio_region_read32(handle->dev.base_addr, mp_reg_offset); | |
| switch (enable) { | |
| case kDifToggleEnabled: | |
| mp_reg = bitfield_field32_write( | |
| mp_reg, FLASH_CTRL_MP_REGION_CFG_0_EN_0_FIELD, kMultiBitBool4True); | |
| break; | |
| case kDifToggleDisabled: | |
| mp_reg = bitfield_field32_write( | |
| mp_reg, FLASH_CTRL_MP_REGION_CFG_0_EN_0_FIELD, kMultiBitBool4False); | |
| break; | |
| default: | |
| return kDifBadArg; | |
| } | |
| mmio_region_write32(handle->dev.base_addr, mp_reg_offset, mp_reg); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_get_data_region_enablement( | |
| const dif_flash_ctrl_state_t *handle, uint32_t region, | |
| dif_toggle_t *enabled_out) { | |
| if (handle == NULL || enabled_out == NULL || | |
| region >= FLASH_CTRL_PARAM_NUM_REGIONS) { | |
| return kDifBadArg; | |
| } | |
| ptrdiff_t mp_reg_offset = get_data_region_mp_reg_offset(region); | |
| uint32_t mp_reg = mmio_region_read32(handle->dev.base_addr, mp_reg_offset); | |
| if (bitfield_field32_read(mp_reg, FLASH_CTRL_MP_REGION_CFG_0_EN_0_FIELD) == | |
| kMultiBitBool4True) { | |
| *enabled_out = kDifToggleEnabled; | |
| } else { | |
| *enabled_out = kDifToggleDisabled; | |
| } | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_set_info_region_enablement( | |
| dif_flash_ctrl_state_t *handle, dif_flash_ctrl_info_region_t region, | |
| dif_toggle_t enable) { | |
| if (handle == NULL || region.bank >= FLASH_CTRL_PARAM_REG_NUM_BANKS || | |
| region.partition_id >= FLASH_CTRL_NUM_INFO_TYPES || | |
| region.page > kNumInfoPagesPerBank[region.partition_id]) { | |
| return kDifBadArg; | |
| } | |
| bool locked; | |
| DIF_RETURN_IF_ERROR( | |
| dif_flash_ctrl_info_region_is_locked(handle, region, &locked)); | |
| if (locked) { | |
| return kDifLocked; | |
| } | |
| ptrdiff_t mp_reg_offset = get_info_region_mp_reg_offset(region); | |
| uint32_t mp_reg = mmio_region_read32(handle->dev.base_addr, mp_reg_offset); | |
| switch (enable) { | |
| case kDifToggleEnabled: | |
| mp_reg = bitfield_field32_write( | |
| mp_reg, FLASH_CTRL_BANK0_INFO0_PAGE_CFG_0_EN_0_FIELD, | |
| kMultiBitBool4True); | |
| break; | |
| case kDifToggleDisabled: | |
| mp_reg = bitfield_field32_write( | |
| mp_reg, FLASH_CTRL_BANK0_INFO0_PAGE_CFG_0_EN_0_FIELD, | |
| kMultiBitBool4False); | |
| break; | |
| default: | |
| return kDifBadArg; | |
| } | |
| mmio_region_write32(handle->dev.base_addr, mp_reg_offset, mp_reg); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_get_info_region_enablement( | |
| const dif_flash_ctrl_state_t *handle, dif_flash_ctrl_info_region_t region, | |
| dif_toggle_t *enabled_out) { | |
| if (handle == NULL || enabled_out == NULL || | |
| region.bank >= FLASH_CTRL_PARAM_REG_NUM_BANKS || | |
| region.partition_id >= FLASH_CTRL_NUM_INFO_TYPES || | |
| region.page > kNumInfoPagesPerBank[region.partition_id]) { | |
| return kDifBadArg; | |
| } | |
| ptrdiff_t mp_reg_offset = get_info_region_mp_reg_offset(region); | |
| uint32_t mp_reg = mmio_region_read32(handle->dev.base_addr, mp_reg_offset); | |
| if (bitfield_field32_read(mp_reg, FLASH_CTRL_MP_REGION_CFG_0_EN_0_FIELD) == | |
| kMultiBitBool4True) { | |
| *enabled_out = kDifToggleEnabled; | |
| } else { | |
| *enabled_out = kDifToggleDisabled; | |
| } | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_set_default_region_properties( | |
| dif_flash_ctrl_state_t *handle, | |
| dif_flash_ctrl_region_properties_t properties) { | |
| if (handle == NULL) { | |
| return kDifBadArg; | |
| } | |
| uint32_t reg = 0; | |
| reg = bitfield_field32_write(reg, FLASH_CTRL_DEFAULT_REGION_RD_EN_FIELD, | |
| properties.rd_en); | |
| reg = bitfield_field32_write(reg, FLASH_CTRL_DEFAULT_REGION_PROG_EN_FIELD, | |
| properties.prog_en); | |
| reg = bitfield_field32_write(reg, FLASH_CTRL_DEFAULT_REGION_ERASE_EN_FIELD, | |
| properties.erase_en); | |
| reg = bitfield_field32_write(reg, FLASH_CTRL_DEFAULT_REGION_SCRAMBLE_EN_FIELD, | |
| properties.scramble_en); | |
| reg = bitfield_field32_write(reg, FLASH_CTRL_DEFAULT_REGION_ECC_EN_FIELD, | |
| properties.ecc_en); | |
| reg = bitfield_field32_write(reg, FLASH_CTRL_DEFAULT_REGION_HE_EN_FIELD, | |
| properties.high_endurance_en); | |
| mmio_region_write32(handle->dev.base_addr, | |
| FLASH_CTRL_DEFAULT_REGION_REG_OFFSET, reg); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_get_default_region_properties( | |
| const dif_flash_ctrl_state_t *handle, | |
| dif_flash_ctrl_region_properties_t *properties_out) { | |
| if (handle == NULL || properties_out == NULL) { | |
| return kDifBadArg; | |
| } | |
| const uint32_t reg = mmio_region_read32(handle->dev.base_addr, | |
| FLASH_CTRL_DEFAULT_REGION_REG_OFFSET); | |
| dif_flash_ctrl_region_properties_t properties = { | |
| .rd_en = | |
| bitfield_field32_read(reg, FLASH_CTRL_DEFAULT_REGION_RD_EN_FIELD), | |
| .prog_en = | |
| bitfield_field32_read(reg, FLASH_CTRL_DEFAULT_REGION_PROG_EN_FIELD), | |
| .erase_en = | |
| bitfield_field32_read(reg, FLASH_CTRL_DEFAULT_REGION_ERASE_EN_FIELD), | |
| .scramble_en = bitfield_field32_read( | |
| reg, FLASH_CTRL_DEFAULT_REGION_SCRAMBLE_EN_FIELD), | |
| .ecc_en = | |
| bitfield_field32_read(reg, FLASH_CTRL_DEFAULT_REGION_ECC_EN_FIELD), | |
| .high_endurance_en = | |
| bitfield_field32_read(reg, FLASH_CTRL_DEFAULT_REGION_HE_EN_FIELD), | |
| }; | |
| *properties_out = properties; | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_set_data_region_properties( | |
| dif_flash_ctrl_state_t *handle, uint32_t region, | |
| dif_flash_ctrl_data_region_properties_t config) { | |
| const uint32_t page_limit = | |
| FLASH_CTRL_PARAM_REG_NUM_BANKS * FLASH_CTRL_PARAM_REG_PAGES_PER_BANK; | |
| if (handle == NULL || region >= FLASH_CTRL_PARAM_NUM_REGIONS || | |
| config.base + config.size > page_limit) { | |
| return kDifBadArg; | |
| } | |
| bool locked; | |
| DIF_RETURN_IF_ERROR( | |
| dif_flash_ctrl_data_region_is_locked(handle, region, &locked)); | |
| if (locked) { | |
| return kDifLocked; | |
| } | |
| ptrdiff_t mp_reg_offset = get_data_region_mp_reg_offset(region); | |
| uint32_t reg = mmio_region_read32(handle->dev.base_addr, mp_reg_offset); | |
| reg = bitfield_field32_write(reg, FLASH_CTRL_MP_REGION_CFG_0_RD_EN_0_FIELD, | |
| config.properties.rd_en); | |
| reg = bitfield_field32_write(reg, FLASH_CTRL_MP_REGION_CFG_0_PROG_EN_0_FIELD, | |
| config.properties.prog_en); | |
| reg = bitfield_field32_write(reg, FLASH_CTRL_MP_REGION_CFG_0_ERASE_EN_0_FIELD, | |
| config.properties.erase_en); | |
| reg = bitfield_field32_write(reg, | |
| FLASH_CTRL_MP_REGION_CFG_0_SCRAMBLE_EN_0_FIELD, | |
| config.properties.scramble_en); | |
| reg = bitfield_field32_write(reg, FLASH_CTRL_MP_REGION_CFG_0_ECC_EN_0_FIELD, | |
| config.properties.ecc_en); | |
| reg = bitfield_field32_write(reg, FLASH_CTRL_MP_REGION_CFG_0_HE_EN_0_FIELD, | |
| config.properties.high_endurance_en); | |
| mmio_region_write32(handle->dev.base_addr, mp_reg_offset, reg); | |
| // size and base are stored in different registers | |
| mp_reg_offset = get_data_region_reg_offset(region); | |
| reg = bitfield_field32_write(0, FLASH_CTRL_MP_REGION_0_BASE_0_FIELD, | |
| config.base); | |
| reg = bitfield_field32_write(reg, FLASH_CTRL_MP_REGION_0_SIZE_0_FIELD, | |
| config.size); | |
| mmio_region_write32(handle->dev.base_addr, mp_reg_offset, reg); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_get_data_region_properties( | |
| const dif_flash_ctrl_state_t *handle, uint32_t region, | |
| dif_flash_ctrl_data_region_properties_t *config_out) { | |
| if (handle == NULL || config_out == NULL || | |
| region >= FLASH_CTRL_PARAM_NUM_REGIONS) { | |
| return kDifBadArg; | |
| } | |
| ptrdiff_t mp_reg_offset = get_data_region_mp_reg_offset(region); | |
| uint32_t reg = mmio_region_read32(handle->dev.base_addr, mp_reg_offset); | |
| dif_flash_ctrl_data_region_properties_t config; | |
| config.properties.rd_en = | |
| bitfield_field32_read(reg, FLASH_CTRL_MP_REGION_CFG_0_RD_EN_0_FIELD); | |
| config.properties.prog_en = | |
| bitfield_field32_read(reg, FLASH_CTRL_MP_REGION_CFG_0_PROG_EN_0_FIELD); | |
| config.properties.erase_en = | |
| bitfield_field32_read(reg, FLASH_CTRL_MP_REGION_CFG_0_ERASE_EN_0_FIELD); | |
| config.properties.scramble_en = bitfield_field32_read( | |
| reg, FLASH_CTRL_MP_REGION_CFG_0_SCRAMBLE_EN_0_FIELD); | |
| config.properties.ecc_en = | |
| bitfield_field32_read(reg, FLASH_CTRL_MP_REGION_CFG_0_ECC_EN_0_FIELD); | |
| config.properties.high_endurance_en = | |
| bitfield_field32_read(reg, FLASH_CTRL_MP_REGION_CFG_0_HE_EN_0_FIELD); | |
| mp_reg_offset = get_data_region_reg_offset(region); | |
| reg = mmio_region_read32(handle->dev.base_addr, mp_reg_offset); | |
| config.base = bitfield_field32_read(reg, FLASH_CTRL_MP_REGION_0_BASE_0_FIELD); | |
| config.size = bitfield_field32_read(reg, FLASH_CTRL_MP_REGION_0_SIZE_0_FIELD); | |
| *config_out = config; | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_set_info_region_properties( | |
| dif_flash_ctrl_state_t *handle, dif_flash_ctrl_info_region_t region, | |
| dif_flash_ctrl_region_properties_t properties) { | |
| if (handle == NULL || region.bank >= FLASH_CTRL_PARAM_REG_NUM_BANKS || | |
| region.partition_id >= FLASH_CTRL_NUM_INFO_TYPES || | |
| region.page > kNumInfoPagesPerBank[region.partition_id]) { | |
| return kDifBadArg; | |
| } | |
| bool locked; | |
| DIF_RETURN_IF_ERROR( | |
| dif_flash_ctrl_info_region_is_locked(handle, region, &locked)); | |
| if (locked) { | |
| return kDifLocked; | |
| } | |
| ptrdiff_t mp_reg_offset = get_info_region_mp_reg_offset(region); | |
| uint32_t reg = mmio_region_read32(handle->dev.base_addr, mp_reg_offset); | |
| reg = bitfield_field32_write(reg, FLASH_CTRL_MP_REGION_CFG_0_RD_EN_0_FIELD, | |
| properties.rd_en); | |
| reg = bitfield_field32_write(reg, FLASH_CTRL_MP_REGION_CFG_0_PROG_EN_0_FIELD, | |
| properties.prog_en); | |
| reg = bitfield_field32_write(reg, FLASH_CTRL_MP_REGION_CFG_0_ERASE_EN_0_FIELD, | |
| properties.erase_en); | |
| reg = bitfield_field32_write(reg, | |
| FLASH_CTRL_MP_REGION_CFG_0_SCRAMBLE_EN_0_FIELD, | |
| properties.scramble_en); | |
| reg = bitfield_field32_write(reg, FLASH_CTRL_MP_REGION_CFG_0_ECC_EN_0_FIELD, | |
| properties.ecc_en); | |
| reg = bitfield_field32_write(reg, FLASH_CTRL_MP_REGION_CFG_0_HE_EN_0_FIELD, | |
| properties.high_endurance_en); | |
| mmio_region_write32(handle->dev.base_addr, mp_reg_offset, reg); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_get_info_region_properties( | |
| const dif_flash_ctrl_state_t *handle, dif_flash_ctrl_info_region_t region, | |
| dif_flash_ctrl_region_properties_t *properties_out) { | |
| if (handle == NULL || properties_out == NULL || | |
| region.bank >= FLASH_CTRL_PARAM_REG_NUM_BANKS || | |
| region.partition_id >= FLASH_CTRL_NUM_INFO_TYPES || | |
| region.page > kNumInfoPagesPerBank[region.partition_id]) { | |
| return kDifBadArg; | |
| } | |
| ptrdiff_t mp_reg_offset = get_info_region_mp_reg_offset(region); | |
| const uint32_t reg = mmio_region_read32(handle->dev.base_addr, mp_reg_offset); | |
| dif_flash_ctrl_region_properties_t properties; | |
| properties.rd_en = bitfield_field32_read( | |
| reg, FLASH_CTRL_BANK0_INFO0_PAGE_CFG_0_RD_EN_0_FIELD); | |
| properties.prog_en = bitfield_field32_read( | |
| reg, FLASH_CTRL_BANK0_INFO0_PAGE_CFG_0_PROG_EN_0_FIELD); | |
| properties.erase_en = bitfield_field32_read( | |
| reg, FLASH_CTRL_BANK0_INFO0_PAGE_CFG_0_ERASE_EN_0_FIELD); | |
| properties.scramble_en = bitfield_field32_read( | |
| reg, FLASH_CTRL_BANK0_INFO0_PAGE_CFG_0_SCRAMBLE_EN_0_FIELD); | |
| properties.ecc_en = bitfield_field32_read( | |
| reg, FLASH_CTRL_BANK0_INFO0_PAGE_CFG_0_ECC_EN_0_FIELD); | |
| properties.high_endurance_en = bitfield_field32_read( | |
| reg, FLASH_CTRL_BANK0_INFO0_PAGE_CFG_0_HE_EN_0_FIELD); | |
| *properties_out = properties; | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_lock_data_region_properties( | |
| dif_flash_ctrl_state_t *handle, uint32_t region) { | |
| if (handle == NULL || region >= FLASH_CTRL_PARAM_NUM_REGIONS) { | |
| return kDifBadArg; | |
| } | |
| bool locked; | |
| DIF_RETURN_IF_ERROR( | |
| dif_flash_ctrl_data_region_is_locked(handle, region, &locked)); | |
| if (locked) { | |
| return kDifOk; | |
| } | |
| uint32_t lock_reg_offset = get_data_region_lock_reg_offset(region); | |
| uint32_t reg = bitfield_bit32_write( | |
| 0, FLASH_CTRL_REGION_CFG_REGWEN_0_REGION_0_BIT, false); | |
| mmio_region_write32(handle->dev.base_addr, lock_reg_offset, reg); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_lock_info_region_properties( | |
| dif_flash_ctrl_state_t *handle, dif_flash_ctrl_info_region_t region) { | |
| if (handle == NULL || region.bank >= FLASH_CTRL_PARAM_REG_NUM_BANKS || | |
| region.partition_id >= FLASH_CTRL_NUM_INFO_TYPES || | |
| region.page > kNumInfoPagesPerBank[region.partition_id]) { | |
| return kDifBadArg; | |
| } | |
| bool locked; | |
| DIF_RETURN_IF_ERROR( | |
| dif_flash_ctrl_info_region_is_locked(handle, region, &locked)); | |
| if (locked) { | |
| return kDifOk; | |
| } | |
| uint32_t lock_reg_offset = get_info_region_lock_reg_offset(region); | |
| uint32_t reg = bitfield_bit32_write( | |
| 0, FLASH_CTRL_BANK0_INFO0_REGWEN_0_REGION_0_BIT, false); | |
| mmio_region_write32(handle->dev.base_addr, lock_reg_offset, reg); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_data_region_is_locked( | |
| const dif_flash_ctrl_state_t *handle, uint32_t region, bool *locked_out) { | |
| if (handle == NULL || locked_out == NULL || | |
| region >= FLASH_CTRL_PARAM_NUM_REGIONS) { | |
| return kDifBadArg; | |
| } | |
| uint32_t lock_reg_offset = get_data_region_lock_reg_offset(region); | |
| uint32_t reg = mmio_region_read32(handle->dev.base_addr, lock_reg_offset); | |
| *locked_out = | |
| !bitfield_bit32_read(reg, FLASH_CTRL_REGION_CFG_REGWEN_0_REGION_0_BIT); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_info_region_is_locked( | |
| const dif_flash_ctrl_state_t *handle, dif_flash_ctrl_info_region_t region, | |
| bool *locked_out) { | |
| if (handle == NULL || locked_out == NULL || | |
| region.bank >= FLASH_CTRL_PARAM_REG_NUM_BANKS || | |
| region.partition_id >= FLASH_CTRL_NUM_INFO_TYPES || | |
| region.page > kNumInfoPagesPerBank[region.partition_id]) { | |
| return kDifBadArg; | |
| } | |
| uint32_t lock_reg_offset = get_info_region_lock_reg_offset(region); | |
| uint32_t reg = mmio_region_read32(handle->dev.base_addr, lock_reg_offset); | |
| *locked_out = | |
| !bitfield_bit32_read(reg, FLASH_CTRL_BANK0_INFO0_REGWEN_0_REGION_0_BIT); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_set_bank_erase_enablement( | |
| dif_flash_ctrl_state_t *handle, uint32_t bank, dif_toggle_t enable) { | |
| if (handle == NULL || bank >= FLASH_CTRL_PARAM_REG_NUM_BANKS) { | |
| return kDifBadArg; | |
| } | |
| bool locked; | |
| DIF_RETURN_IF_ERROR( | |
| dif_flash_ctrl_bank_configuration_is_locked(handle, &locked)); | |
| if (locked) { | |
| return kDifLocked; | |
| } | |
| bitfield_bit32_index_t index = bank; | |
| uint32_t value = mmio_region_read32( | |
| handle->dev.base_addr, FLASH_CTRL_MP_BANK_CFG_SHADOWED_REG_OFFSET); | |
| switch (enable) { | |
| case kDifToggleEnabled: | |
| value = bitfield_bit32_write(value, index, true); | |
| break; | |
| case kDifToggleDisabled: | |
| value = bitfield_bit32_write(value, index, false); | |
| break; | |
| default: | |
| return kDifBadArg; | |
| } | |
| mmio_region_write32_shadowed( | |
| handle->dev.base_addr, FLASH_CTRL_MP_BANK_CFG_SHADOWED_REG_OFFSET, value); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_get_bank_erase_enablement( | |
| const dif_flash_ctrl_state_t *handle, uint32_t bank, | |
| dif_toggle_t *enabled_out) { | |
| if (handle == NULL || enabled_out == NULL || | |
| bank >= FLASH_CTRL_PARAM_REG_NUM_BANKS) { | |
| return kDifBadArg; | |
| } | |
| uint32_t reg = mmio_region_read32(handle->dev.base_addr, | |
| FLASH_CTRL_MP_BANK_CFG_SHADOWED_REG_OFFSET); | |
| bitfield_bit32_index_t index = bank; | |
| bool enabled = bitfield_bit32_read(reg, index); | |
| if (enabled) { | |
| *enabled_out = kDifToggleEnabled; | |
| } else { | |
| *enabled_out = kDifToggleDisabled; | |
| } | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_lock_bank_configuration( | |
| dif_flash_ctrl_state_t *handle) { | |
| if (handle == NULL) { | |
| return kDifBadArg; | |
| } | |
| bool locked; | |
| DIF_RETURN_IF_ERROR( | |
| dif_flash_ctrl_bank_configuration_is_locked(handle, &locked)); | |
| if (locked) { | |
| return kDifLocked; | |
| } | |
| uint32_t reg = | |
| bitfield_bit32_write(0, FLASH_CTRL_BANK_CFG_REGWEN_BANK_BIT, false); | |
| mmio_region_write32(handle->dev.base_addr, | |
| FLASH_CTRL_BANK_CFG_REGWEN_REG_OFFSET, reg); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_bank_configuration_is_locked( | |
| const dif_flash_ctrl_state_t *handle, bool *locked_out) { | |
| if (handle == NULL || locked_out == NULL) { | |
| return kDifBadArg; | |
| } | |
| const uint32_t reg = mmio_region_read32( | |
| handle->dev.base_addr, FLASH_CTRL_BANK_CFG_REGWEN_REG_OFFSET); | |
| *locked_out = !bitfield_bit32_read(reg, FLASH_CTRL_BANK_CFG_REGWEN_BANK_BIT); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_set_prog_fifo_watermark( | |
| dif_flash_ctrl_state_t *handle, uint32_t level) { | |
| if (handle == NULL || level > FLASH_CTRL_FIFO_LVL_PROG_MASK) { | |
| return kDifBadArg; | |
| } | |
| uint32_t reg = | |
| mmio_region_read32(handle->dev.base_addr, FLASH_CTRL_FIFO_LVL_REG_OFFSET); | |
| reg = bitfield_field32_write(reg, FLASH_CTRL_FIFO_LVL_PROG_FIELD, level); | |
| mmio_region_write32(handle->dev.base_addr, FLASH_CTRL_FIFO_LVL_REG_OFFSET, | |
| reg); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_set_read_fifo_watermark( | |
| dif_flash_ctrl_state_t *handle, uint32_t level) { | |
| if (handle == NULL || level > FLASH_CTRL_FIFO_LVL_RD_MASK) { | |
| return kDifBadArg; | |
| } | |
| uint32_t reg = | |
| mmio_region_read32(handle->dev.base_addr, FLASH_CTRL_FIFO_LVL_REG_OFFSET); | |
| reg = bitfield_field32_write(reg, FLASH_CTRL_FIFO_LVL_RD_FIELD, level); | |
| mmio_region_write32(handle->dev.base_addr, FLASH_CTRL_FIFO_LVL_REG_OFFSET, | |
| reg); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_get_fifo_watermarks( | |
| const dif_flash_ctrl_state_t *handle, uint32_t *prog_out, | |
| uint32_t *read_out) { | |
| if (handle == NULL) { | |
| return kDifBadArg; | |
| } | |
| const uint32_t reg = | |
| mmio_region_read32(handle->dev.base_addr, FLASH_CTRL_FIFO_LVL_REG_OFFSET); | |
| if (prog_out != NULL) { | |
| *prog_out = bitfield_field32_read(reg, FLASH_CTRL_FIFO_LVL_PROG_FIELD); | |
| } | |
| if (read_out != NULL) { | |
| *read_out = bitfield_field32_read(reg, FLASH_CTRL_FIFO_LVL_RD_FIELD); | |
| } | |
| return kDifOk; | |
| } | |
| // TODO: Allow splitting up turning the reset on and off? | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_reset_fifos(dif_flash_ctrl_state_t *handle) { | |
| if (handle == NULL) { | |
| return kDifBadArg; | |
| } | |
| uint32_t reg = bitfield_bit32_write(0, FLASH_CTRL_FIFO_RST_EN_BIT, true); | |
| mmio_region_write32(handle->dev.base_addr, FLASH_CTRL_FIFO_RST_REG_OFFSET, | |
| reg); | |
| reg = bitfield_bit32_write(0, FLASH_CTRL_FIFO_RST_EN_BIT, false); | |
| mmio_region_write32(handle->dev.base_addr, FLASH_CTRL_FIFO_RST_REG_OFFSET, | |
| reg); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_get_faults(const dif_flash_ctrl_state_t *handle, | |
| dif_flash_ctrl_faults_t *faults_out) { | |
| if (handle == NULL || faults_out == NULL) { | |
| return kDifBadArg; | |
| } | |
| uint32_t reg = mmio_region_read32(handle->dev.base_addr, | |
| FLASH_CTRL_FAULT_STATUS_REG_OFFSET); | |
| dif_flash_ctrl_faults_t faults; | |
| faults.memory_properties_error = | |
| bitfield_bit32_read(reg, FLASH_CTRL_FAULT_STATUS_MP_ERR_BIT); | |
| faults.read_error = | |
| bitfield_bit32_read(reg, FLASH_CTRL_FAULT_STATUS_RD_ERR_BIT); | |
| faults.prog_window_error = | |
| bitfield_bit32_read(reg, FLASH_CTRL_FAULT_STATUS_PROG_WIN_ERR_BIT); | |
| faults.prog_type_error = | |
| bitfield_bit32_read(reg, FLASH_CTRL_FAULT_STATUS_PROG_TYPE_ERR_BIT); | |
| faults.host_gnt_error = | |
| bitfield_bit32_read(reg, FLASH_CTRL_FAULT_STATUS_HOST_GNT_ERR_BIT); | |
| reg = mmio_region_read32(handle->dev.base_addr, | |
| FLASH_CTRL_STD_FAULT_STATUS_REG_OFFSET); | |
| faults.register_integrity_error = | |
| bitfield_bit32_read(reg, FLASH_CTRL_STD_FAULT_STATUS_REG_INTG_ERR_BIT); | |
| faults.phy_integrity_error = | |
| bitfield_bit32_read(reg, FLASH_CTRL_STD_FAULT_STATUS_PROG_INTG_ERR_BIT); | |
| faults.lifecycle_manager_error = | |
| bitfield_bit32_read(reg, FLASH_CTRL_STD_FAULT_STATUS_LCMGR_ERR_BIT); | |
| faults.shadow_storage_error = | |
| bitfield_bit32_read(reg, FLASH_CTRL_STD_FAULT_STATUS_STORAGE_ERR_BIT); | |
| *faults_out = faults; | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_get_ecc_errors( | |
| const dif_flash_ctrl_state_t *handle, uint32_t bank, | |
| dif_flash_ctrl_ecc_errors_t *errors_out) { | |
| if (handle == NULL || errors_out == NULL || | |
| bank > FLASH_CTRL_PARAM_REG_NUM_BANKS) { | |
| return kDifBadArg; | |
| } | |
| bitfield_field32_t error_count_field; | |
| uint32_t last_addr_reg_offset; | |
| #if FLASH_CTRL_PARAM_REG_NUM_BANKS > 2 | |
| #error "Revise this function to handle more banks." | |
| #endif | |
| if (bank == 0) { | |
| error_count_field = | |
| FLASH_CTRL_ECC_SINGLE_ERR_CNT_ECC_SINGLE_ERR_CNT_0_FIELD; | |
| last_addr_reg_offset = FLASH_CTRL_ECC_SINGLE_ERR_ADDR_0_REG_OFFSET; | |
| } else { | |
| error_count_field = | |
| FLASH_CTRL_ECC_SINGLE_ERR_CNT_ECC_SINGLE_ERR_CNT_1_FIELD; | |
| last_addr_reg_offset = FLASH_CTRL_ECC_SINGLE_ERR_ADDR_1_REG_OFFSET; | |
| } | |
| uint32_t reg = mmio_region_read32(handle->dev.base_addr, | |
| FLASH_CTRL_ECC_SINGLE_ERR_CNT_REG_OFFSET); | |
| errors_out->single_bit_error_count = | |
| bitfield_field32_read(reg, error_count_field); | |
| errors_out->last_error_address = | |
| mmio_region_read32(handle->dev.base_addr, last_addr_reg_offset); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_get_phy_status( | |
| const dif_flash_ctrl_state_t *handle, | |
| dif_flash_ctrl_phy_status_t *status_out) { | |
| if (handle == NULL || status_out == NULL) { | |
| return kDifBadArg; | |
| } | |
| const uint32_t reg = mmio_region_read32(handle->dev.base_addr, | |
| FLASH_CTRL_PHY_STATUS_REG_OFFSET); | |
| dif_flash_ctrl_phy_status_t status = { | |
| .phy_init_wip = | |
| bitfield_bit32_read(reg, FLASH_CTRL_PHY_STATUS_INIT_WIP_BIT), | |
| .prog_normal_available = | |
| bitfield_bit32_read(reg, FLASH_CTRL_PHY_STATUS_PROG_NORMAL_AVAIL_BIT), | |
| .prog_repair_available = | |
| bitfield_bit32_read(reg, FLASH_CTRL_PHY_STATUS_PROG_REPAIR_AVAIL_BIT), | |
| }; | |
| *status_out = status; | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_set_scratch(dif_flash_ctrl_state_t *handle, | |
| uint32_t value) { | |
| if (handle == NULL) { | |
| return kDifBadArg; | |
| } | |
| mmio_region_write32(handle->dev.base_addr, FLASH_CTRL_SCRATCH_REG_OFFSET, | |
| value); | |
| return kDifOk; | |
| } | |
| OT_WARN_UNUSED_RESULT | |
| dif_result_t dif_flash_ctrl_get_scratch(const dif_flash_ctrl_state_t *handle, | |
| uint32_t *value_out) { | |
| if (handle == NULL || value_out == NULL) { | |
| return kDifBadArg; | |
| } | |
| *value_out = | |
| mmio_region_read32(handle->dev.base_addr, FLASH_CTRL_SCRATCH_REG_OFFSET); | |
| return kDifOk; | |
| } |