blob: 6b14ad5a62456899f29a48e9b3946e09805ba683 [file] [log] [blame]
// 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;
}