blob: b913dc1a30ec74a3c0ccbc1b84b6de5f057a4142 [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_otp_ctrl.h"
#include <stddef.h>
#include "sw/device/lib/base/bitfield.h"
#include "sw/device/lib/base/memory.h"
#include "otp_ctrl_regs.h" // Generated.
dif_otp_ctrl_result_t dif_otp_ctrl_init(dif_otp_ctrl_params_t params,
dif_otp_ctrl_t *otp) {
if (otp == NULL) {
return kDifOtpCtrlBadArg;
}
otp->params = params;
return kDifOtpCtrlOk;
}
/**
* Checks if integrity/consistency-check-related operations are locked.
*
* This is a convenience function to avoid superfluous error-checking in all the
* functions that can be locked out by this register.
*/
static bool checks_are_locked(const dif_otp_ctrl_t *otp) {
uint32_t locked = mmio_region_read32(otp->params.base_addr_core,
OTP_CTRL_CHECK_REGWEN_REG_OFFSET);
return !bitfield_bit32_read(locked, OTP_CTRL_CHECK_REGWEN_CHECK_REGWEN_BIT);
}
dif_otp_ctrl_lockable_result_t dif_otp_ctrl_configure(
const dif_otp_ctrl_t *otp, dif_otp_ctrl_config_t config) {
if (otp == NULL) {
return kDifOtpCtrlLockableBadArg;
}
if (checks_are_locked(otp)) {
return kDifOtpCtrlLockableLocked;
}
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_CHECK_TIMEOUT_REG_OFFSET, config.check_timeout);
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_INTEGRITY_CHECK_PERIOD_REG_OFFSET,
config.integrity_period_mask);
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_CONSISTENCY_CHECK_PERIOD_REG_OFFSET,
config.consistency_period_mask);
return kDifOtpCtrlLockableOk;
}
dif_otp_ctrl_lockable_result_t dif_otp_ctrl_check_integrity(
const dif_otp_ctrl_t *otp) {
if (otp == NULL) {
return kDifOtpCtrlLockableBadArg;
}
if (checks_are_locked(otp)) {
return kDifOtpCtrlLockableLocked;
}
uint32_t reg =
bitfield_bit32_write(0, OTP_CTRL_CHECK_TRIGGER_INTEGRITY_BIT, true);
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_CHECK_TRIGGER_REG_OFFSET, reg);
return kDifOtpCtrlLockableOk;
}
dif_otp_ctrl_lockable_result_t dif_otp_ctrl_check_consistency(
const dif_otp_ctrl_t *otp) {
if (otp == NULL) {
return kDifOtpCtrlLockableBadArg;
}
if (checks_are_locked(otp)) {
return kDifOtpCtrlLockableLocked;
}
uint32_t reg =
bitfield_bit32_write(0, OTP_CTRL_CHECK_TRIGGER_CONSISTENCY_BIT, true);
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_CHECK_TRIGGER_REG_OFFSET, reg);
return kDifOtpCtrlLockableOk;
}
dif_otp_ctrl_result_t dif_otp_ctrl_lock_config(const dif_otp_ctrl_t *otp) {
if (otp == NULL) {
return kDifOtpCtrlBadArg;
}
uint32_t reg =
bitfield_bit32_write(0, OTP_CTRL_CHECK_REGWEN_CHECK_REGWEN_BIT, true);
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_CHECK_REGWEN_REG_OFFSET, reg);
return kDifOtpCtrlOk;
}
dif_otp_ctrl_result_t dif_otp_ctrl_config_is_locked(const dif_otp_ctrl_t *otp,
bool *is_locked) {
if (otp == NULL || is_locked == NULL) {
return kDifOtpCtrlBadArg;
}
*is_locked = checks_are_locked(otp);
return kDifOtpCtrlOk;
}
static bool sw_read_lock_reg_offset(dif_otp_ctrl_partition_t partition,
ptrdiff_t *reg_offset,
bitfield_bit32_index_t *index) {
switch (partition) {
case kDifOtpCtrlPartitionCreatorSwCfg:
*reg_offset = OTP_CTRL_CREATOR_SW_CFG_READ_LOCK_REG_OFFSET;
*index = OTP_CTRL_CREATOR_SW_CFG_READ_LOCK_CREATOR_SW_CFG_READ_LOCK_BIT;
break;
case kDifOtpCtrlPartitionOwnerSwCfg:
*reg_offset = OTP_CTRL_OWNER_SW_CFG_READ_LOCK_REG_OFFSET;
*index = OTP_CTRL_OWNER_SW_CFG_READ_LOCK_OWNER_SW_CFG_READ_LOCK_BIT;
break;
default:
return false;
}
return true;
}
dif_otp_ctrl_result_t dif_otp_ctrl_lock_reading(
const dif_otp_ctrl_t *otp, dif_otp_ctrl_partition_t partition) {
if (otp == NULL) {
return kDifOtpCtrlBadArg;
}
ptrdiff_t offset;
bitfield_bit32_index_t index;
if (!sw_read_lock_reg_offset(partition, &offset, &index)) {
return kDifOtpCtrlBadArg;
}
uint32_t reg = bitfield_bit32_write(0, index, true);
mmio_region_write32(otp->params.base_addr_core, offset, reg);
return kDifOtpCtrlOk;
}
dif_otp_ctrl_result_t dif_otp_ctrl_reading_is_locked(
const dif_otp_ctrl_t *otp, dif_otp_ctrl_partition_t partition,
bool *is_locked) {
if (otp == NULL || is_locked == NULL) {
return kDifOtpCtrlBadArg;
}
ptrdiff_t offset;
bitfield_bit32_index_t index;
if (!sw_read_lock_reg_offset(partition, &offset, &index)) {
return kDifOtpCtrlBadArg;
}
uint32_t reg = mmio_region_read32(otp->params.base_addr_core, offset);
*is_locked = !bitfield_bit32_read(reg, index);
return kDifOtpCtrlOk;
}
static bool irq_index(dif_otp_ctrl_irq_t irq, bitfield_bit32_index_t *index) {
switch (irq) {
case kDifOtpCtrlIrqDone:
*index = OTP_CTRL_INTR_COMMON_OTP_OPERATION_DONE_BIT;
break;
case kDifOtpCtrlIrqError:
*index = OTP_CTRL_INTR_COMMON_OTP_ERROR_BIT;
break;
default:
return false;
}
return true;
}
dif_otp_ctrl_result_t dif_otp_ctrl_irq_is_pending(const dif_otp_ctrl_t *otp,
dif_otp_ctrl_irq_t irq,
bool *is_pending) {
if (otp == NULL || is_pending == NULL) {
return kDifOtpCtrlBadArg;
}
bitfield_bit32_index_t index;
if (!irq_index(irq, &index)) {
return kDifOtpCtrlBadArg;
}
uint32_t reg = mmio_region_read32(otp->params.base_addr_core,
OTP_CTRL_INTR_STATE_REG_OFFSET);
*is_pending = bitfield_bit32_read(reg, index);
return kDifOtpCtrlOk;
}
dif_otp_ctrl_result_t dif_otp_ctrl_irq_acknowledge(const dif_otp_ctrl_t *otp,
dif_otp_ctrl_irq_t irq) {
if (otp == NULL) {
return kDifOtpCtrlBadArg;
}
bitfield_bit32_index_t index;
if (!irq_index(irq, &index)) {
return kDifOtpCtrlBadArg;
}
uint32_t reg = bitfield_bit32_write(0, index, true);
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_INTR_STATE_REG_OFFSET, reg);
return kDifOtpCtrlOk;
}
dif_otp_ctrl_result_t dif_otp_ctrl_irq_get_enabled(
const dif_otp_ctrl_t *otp, dif_otp_ctrl_irq_t irq,
dif_otp_ctrl_toggle_t *state) {
if (otp == NULL || state == NULL) {
return kDifOtpCtrlBadArg;
}
bitfield_bit32_index_t index;
if (!irq_index(irq, &index)) {
return kDifOtpCtrlBadArg;
}
uint32_t reg = mmio_region_read32(otp->params.base_addr_core,
OTP_CTRL_INTR_ENABLE_REG_OFFSET);
*state = bitfield_bit32_read(reg, index) ? kDifOtpCtrlToggleEnabled
: kDifOtpCtrlToggleDisabled;
return kDifOtpCtrlOk;
}
dif_otp_ctrl_result_t dif_otp_ctrl_irq_set_enabled(
const dif_otp_ctrl_t *otp, dif_otp_ctrl_irq_t irq,
dif_otp_ctrl_toggle_t state) {
if (otp == NULL) {
return kDifOtpCtrlBadArg;
}
bitfield_bit32_index_t index;
if (!irq_index(irq, &index)) {
return kDifOtpCtrlBadArg;
}
bool flag;
switch (state) {
case kDifOtpCtrlToggleEnabled:
flag = true;
break;
case kDifOtpCtrlToggleDisabled:
flag = false;
break;
default:
return kDifOtpCtrlBadArg;
}
uint32_t reg = mmio_region_read32(otp->params.base_addr_core,
OTP_CTRL_INTR_ENABLE_REG_OFFSET);
reg = bitfield_bit32_write(reg, index, flag);
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_INTR_ENABLE_REG_OFFSET, reg);
return kDifOtpCtrlOk;
}
dif_otp_ctrl_result_t dif_otp_ctrl_irq_force(const dif_otp_ctrl_t *otp,
dif_otp_ctrl_irq_t irq) {
if (otp == NULL) {
return kDifOtpCtrlBadArg;
}
bitfield_bit32_index_t index;
if (!irq_index(irq, &index)) {
return kDifOtpCtrlBadArg;
}
uint32_t reg = bitfield_bit32_write(0, index, true);
mmio_region_write32(otp->params.base_addr_core, OTP_CTRL_INTR_TEST_REG_OFFSET,
reg);
return kDifOtpCtrlOk;
}
dif_otp_ctrl_result_t dif_otp_ctrl_irq_disable_all(
const dif_otp_ctrl_t *otp, dif_otp_ctrl_irq_snapshot_t *snapshot) {
if (otp == NULL) {
return kDifOtpCtrlBadArg;
}
if (snapshot != NULL) {
*snapshot = mmio_region_read32(otp->params.base_addr_core,
OTP_CTRL_INTR_ENABLE_REG_OFFSET);
}
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_INTR_ENABLE_REG_OFFSET, 0);
return kDifOtpCtrlOk;
}
dif_otp_ctrl_result_t dif_otp_ctrl_irq_restore_all(
const dif_otp_ctrl_t *otp, const dif_otp_ctrl_irq_snapshot_t *snapshot) {
if (otp == NULL || snapshot == NULL) {
return kDifOtpCtrlBadArg;
}
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_INTR_ENABLE_REG_OFFSET, *snapshot);
return kDifOtpCtrlOk;
}
dif_otp_ctrl_result_t dif_otp_ctrl_get_status(const dif_otp_ctrl_t *otp,
dif_otp_ctrl_status_t *status) {
if (otp == NULL || status == NULL) {
return kDifOtpCtrlBadArg;
}
static const bitfield_bit32_index_t kIndices[] = {
[kDifOtpCtrlStatusCodeCreatorSwCfgError] =
OTP_CTRL_STATUS_CREATOR_SW_CFG_ERROR_BIT,
[kDifOtpCtrlStatusCodeOwnerSwCfgError] =
OTP_CTRL_STATUS_OWNER_SW_CFG_ERROR_BIT,
[kDifOtpCtrlStatusCodeHwCfgError] = OTP_CTRL_STATUS_HW_CFG_ERROR_BIT,
[kDifOtpCtrlStatusCodeLifeCycleError] =
OTP_CTRL_STATUS_LIFE_CYCLE_ERROR_BIT,
[kDifOtpCtrlStatusCodeSecret0Error] = OTP_CTRL_STATUS_SECRET0_ERROR_BIT,
[kDifOtpCtrlStatusCodeSecret1Error] = OTP_CTRL_STATUS_SECRET1_ERROR_BIT,
[kDifOtpCtrlStatusCodeSecret2Error] = OTP_CTRL_STATUS_SECRET2_ERROR_BIT,
[kDifOtpCtrlStatusCodeDaiError] = OTP_CTRL_STATUS_DAI_ERROR_BIT,
[kDifOtpCtrlStatusCodeLciError] = OTP_CTRL_STATUS_LCI_ERROR_BIT,
[kDifOtpCtrlStatusCodeTimeoutError] = OTP_CTRL_STATUS_TIMEOUT_ERROR_BIT,
[kDifOtpCtrlStatusCodeLfsrError] = OTP_CTRL_STATUS_LFSR_FSM_ERROR_BIT,
[kDifOtpCtrlStatusCodeScramblingError] =
OTP_CTRL_STATUS_SCRAMBLING_FSM_ERROR_BIT,
[kDifOtpCtrlStatusCodeKdfError] = OTP_CTRL_STATUS_KEY_DERIV_FSM_ERROR_BIT,
[kDifOtpCtrlStatusCodeBusIntegError] =
OTP_CTRL_STATUS_BUS_INTEG_ERROR_BIT,
[kDifOtpCtrlStatusCodeDaiIdle] = OTP_CTRL_STATUS_DAI_IDLE_BIT,
[kDifOtpCtrlStatusCodeCheckPending] = OTP_CTRL_STATUS_CHECK_PENDING_BIT,
};
status->codes = 0;
uint32_t status_code = mmio_region_read32(otp->params.base_addr_core,
OTP_CTRL_STATUS_REG_OFFSET);
uint32_t error_codes = mmio_region_read32(otp->params.base_addr_core,
OTP_CTRL_ERR_CODE_REG_OFFSET);
for (int i = 0; i < ARRAYSIZE(kIndices); ++i) {
// If the error is not present at all, we clear its cause bit if relevant,
// and bail immediately.
if (!bitfield_bit32_read(status_code, kIndices[i])) {
if (i <= kDifOtpCtrlStatusCodeHasCauseLast) {
status->causes[i] = kDifOtpCtrlErrorOk;
}
continue;
}
status->codes = bitfield_bit32_write(status->codes, i, true);
bitfield_field32_t field;
switch (i) {
case kDifOtpCtrlStatusCodeCreatorSwCfgError:
field = (bitfield_field32_t){
.mask = OTP_CTRL_ERR_CODE_ERR_CODE_0_MASK,
.index = OTP_CTRL_ERR_CODE_ERR_CODE_0_OFFSET,
};
break;
case kDifOtpCtrlStatusCodeOwnerSwCfgError:
field = (bitfield_field32_t){
.mask = OTP_CTRL_ERR_CODE_ERR_CODE_1_MASK,
.index = OTP_CTRL_ERR_CODE_ERR_CODE_1_OFFSET,
};
break;
case kDifOtpCtrlStatusCodeHwCfgError:
field = (bitfield_field32_t){
.mask = OTP_CTRL_ERR_CODE_ERR_CODE_2_MASK,
.index = OTP_CTRL_ERR_CODE_ERR_CODE_2_OFFSET,
};
break;
case kDifOtpCtrlStatusCodeSecret0Error:
field = (bitfield_field32_t){
.mask = OTP_CTRL_ERR_CODE_ERR_CODE_3_MASK,
.index = OTP_CTRL_ERR_CODE_ERR_CODE_3_OFFSET,
};
break;
case kDifOtpCtrlStatusCodeSecret1Error:
field = (bitfield_field32_t){
.mask = OTP_CTRL_ERR_CODE_ERR_CODE_4_MASK,
.index = OTP_CTRL_ERR_CODE_ERR_CODE_4_OFFSET,
};
break;
case kDifOtpCtrlStatusCodeSecret2Error:
field = (bitfield_field32_t){
.mask = OTP_CTRL_ERR_CODE_ERR_CODE_5_MASK,
.index = OTP_CTRL_ERR_CODE_ERR_CODE_5_OFFSET,
};
break;
case kDifOtpCtrlStatusCodeLifeCycleError:
field = (bitfield_field32_t){
.mask = OTP_CTRL_ERR_CODE_ERR_CODE_6_MASK,
.index = OTP_CTRL_ERR_CODE_ERR_CODE_6_OFFSET,
};
break;
case kDifOtpCtrlStatusCodeDaiError:
field = (bitfield_field32_t){
.mask = OTP_CTRL_ERR_CODE_ERR_CODE_7_MASK,
.index = OTP_CTRL_ERR_CODE_ERR_CODE_7_OFFSET,
};
break;
case kDifOtpCtrlStatusCodeLciError:
field = (bitfield_field32_t){
.mask = OTP_CTRL_ERR_CODE_ERR_CODE_8_MASK,
.index = OTP_CTRL_ERR_CODE_ERR_CODE_8_OFFSET,
};
break;
// Not an error status, so there's nothing to do.
default:
continue;
}
dif_otp_ctrl_error_t err;
switch (bitfield_field32_read(error_codes, field)) {
case OTP_CTRL_ERR_CODE_ERR_CODE_0_VALUE_NO_ERROR:
err = kDifOtpCtrlErrorOk;
break;
case OTP_CTRL_ERR_CODE_ERR_CODE_0_VALUE_MACRO_ERROR:
err = kDifOtpCtrlErrorMacroUnspecified;
break;
case OTP_CTRL_ERR_CODE_ERR_CODE_0_VALUE_MACRO_ECC_CORR_ERROR:
err = kDifOtpCtrlErrorMacroRecoverableRead;
break;
case OTP_CTRL_ERR_CODE_ERR_CODE_0_VALUE_MACRO_ECC_UNCORR_ERROR:
err = kDifOtpCtrlErrorMacroUnrecoverableRead;
break;
case OTP_CTRL_ERR_CODE_ERR_CODE_0_VALUE_MACRO_WRITE_BLANK_ERROR:
err = kDifOtpCtrlErrorMacroBlankCheckFailed;
break;
case OTP_CTRL_ERR_CODE_ERR_CODE_0_VALUE_ACCESS_ERROR:
err = kDifOtpCtrlErrorLockedAccess;
break;
case OTP_CTRL_ERR_CODE_ERR_CODE_0_VALUE_CHECK_FAIL_ERROR:
err = kDifOtpCtrlErrorBackgroundCheckFailed;
break;
case OTP_CTRL_ERR_CODE_ERR_CODE_0_VALUE_FSM_STATE_ERROR:
err = kDifOtpCtrlErrorFsmBadState;
break;
default:
return kDifOtpCtrlError;
}
status->causes[i] = err;
}
return kDifOtpCtrlOk;
}
typedef struct partition_info {
/**
* The absolute OTP address at which this partition starts.
*/
uint32_t start_addr;
/**
* The length of this partition, in bytes, including the digest.
*
* If the partition has a digest, it is expected to be at address
* `start_addr + len - sizeof(uint64_t)`.
*/
uint32_t len;
/**
* The alignment mask for this partition.
*
* A valid address for this partition must be such that
* `addr & align_mask == 0`.
*/
uint32_t align_mask;
/**
* Whether this is a software-managed partition with a software-managed
* digest.
*/
bool is_software;
} partition_info_t;
static const partition_info_t kPartitions[] = {
// TODO: These should be provided by the gen'd header.
// See #3904.
[kDifOtpCtrlPartitionCreatorSwCfg] =
{
.start_addr = OTP_CTRL_PARAM_CREATOR_SW_CFG_OFFSET,
.len = OTP_CTRL_PARAM_CREATOR_SW_CFG_SIZE,
.align_mask = 0x3,
.is_software = true,
},
[kDifOtpCtrlPartitionOwnerSwCfg] =
{
.start_addr = OTP_CTRL_PARAM_OWNER_SW_CFG_OFFSET,
.len = OTP_CTRL_PARAM_OWNER_SW_CFG_SIZE,
.align_mask = 0x3,
.is_software = true,
},
[kDifOtpCtrlPartitionHwCfg] =
{
.start_addr = OTP_CTRL_PARAM_HW_CFG_OFFSET,
.len = OTP_CTRL_PARAM_HW_CFG_SIZE,
.align_mask = 0x3,
},
[kDifOtpCtrlPartitionSecret0] =
{
.start_addr = OTP_CTRL_PARAM_SECRET0_OFFSET,
.len = OTP_CTRL_PARAM_SECRET0_SIZE,
.align_mask = 0x7,
},
[kDifOtpCtrlPartitionSecret1] =
{
.start_addr = OTP_CTRL_PARAM_SECRET1_OFFSET,
.len = OTP_CTRL_PARAM_SECRET1_SIZE,
.align_mask = 0x7,
},
[kDifOtpCtrlPartitionSecret2] =
{
.start_addr = OTP_CTRL_PARAM_SECRET2_OFFSET,
.len = OTP_CTRL_PARAM_SECRET2_SIZE,
.align_mask = 0x7,
},
[kDifOtpCtrlPartitionLifeCycle] =
{
.start_addr = OTP_CTRL_PARAM_LIFE_CYCLE_OFFSET,
.len = OTP_CTRL_PARAM_LIFE_CYCLE_SIZE,
.align_mask = 0x3,
},
};
dif_otp_ctrl_dai_result_t dif_otp_ctrl_dai_read_start(
const dif_otp_ctrl_t *otp, dif_otp_ctrl_partition_t partition,
uint32_t address) {
if (otp == NULL || partition >= ARRAYSIZE(kPartitions)) {
return kDifOtpCtrlDaiBadArg;
}
if ((address & kPartitions[partition].align_mask) != 0) {
return kDifOtpCtrlDaiUnaligned;
}
if (address >= kPartitions[partition].len) {
return kDifOtpCtrlDaiOutOfRange;
}
uint32_t busy = mmio_region_read32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET);
if (!bitfield_bit32_read(
busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT)) {
return kDifOtpCtrlDaiBusy;
}
address += kPartitions[partition].start_addr;
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, address);
uint32_t cmd =
bitfield_bit32_write(0, OTP_CTRL_DIRECT_ACCESS_CMD_RD_BIT, true);
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET, cmd);
return kDifOtpCtrlDaiOk;
}
dif_otp_ctrl_dai_result_t dif_otp_ctrl_dai_read32_end(const dif_otp_ctrl_t *otp,
uint32_t *value) {
if (otp == NULL || value == NULL) {
return kDifOtpCtrlDaiBadArg;
}
uint32_t busy = mmio_region_read32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET);
if (!bitfield_bit32_read(
busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT)) {
return kDifOtpCtrlDaiBusy;
}
*value = mmio_region_read32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_RDATA_0_REG_OFFSET);
return kDifOtpCtrlDaiOk;
}
dif_otp_ctrl_dai_result_t dif_otp_ctrl_dai_read64_end(const dif_otp_ctrl_t *otp,
uint64_t *value) {
if (otp == NULL || value == NULL) {
return kDifOtpCtrlDaiBadArg;
}
uint32_t busy = mmio_region_read32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET);
if (!bitfield_bit32_read(
busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT)) {
return kDifOtpCtrlDaiBusy;
}
*value = mmio_region_read32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_RDATA_1_REG_OFFSET);
*value <<= 32;
*value |= mmio_region_read32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_RDATA_0_REG_OFFSET);
return kDifOtpCtrlDaiOk;
}
dif_otp_ctrl_dai_result_t dif_otp_ctrl_dai_program32(
const dif_otp_ctrl_t *otp, dif_otp_ctrl_partition_t partition,
uint32_t address, uint32_t value) {
if (otp == NULL || partition >= ARRAYSIZE(kPartitions)) {
return kDifOtpCtrlDaiBadArg;
}
// Ensure that we are writing to a 32-bit-access partition by checking that
// the alignment mask is 0b11.
//
// Note furthermore that the LC partition is *not* writeable, so we eject
// here.
if (kPartitions[partition].align_mask != 0x3 ||
partition == kDifOtpCtrlPartitionLifeCycle) {
return kDifOtpCtrlDaiBadPartition;
}
if ((address & kPartitions[partition].align_mask) != 0) {
return kDifOtpCtrlDaiUnaligned;
}
// NOTE: The bounds check is tightened here, since we disallow writing the
// digest directly.
size_t digest_size = sizeof(uint64_t);
if (address >= kPartitions[partition].len - digest_size) {
return kDifOtpCtrlDaiOutOfRange;
}
uint32_t busy = mmio_region_read32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET);
if (!bitfield_bit32_read(
busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT)) {
return kDifOtpCtrlDaiBusy;
}
address += kPartitions[partition].start_addr;
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, address);
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET, value);
uint32_t cmd =
bitfield_bit32_write(0, OTP_CTRL_DIRECT_ACCESS_CMD_WR_BIT, true);
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET, cmd);
return kDifOtpCtrlDaiOk;
}
dif_otp_ctrl_dai_result_t dif_otp_ctrl_dai_program64(
const dif_otp_ctrl_t *otp, dif_otp_ctrl_partition_t partition,
uint32_t address, uint64_t value) {
if (otp == NULL || partition >= ARRAYSIZE(kPartitions)) {
return kDifOtpCtrlDaiBadArg;
}
// Ensure that we are writing to a 32-bit-access partition by checking that
// the alignment mask is 0b11.
if (kPartitions[partition].align_mask != 0x7) {
return kDifOtpCtrlDaiBadPartition;
}
if ((address & kPartitions[partition].align_mask) != 0) {
return kDifOtpCtrlDaiUnaligned;
}
// NOTE: The bounds check is tightened here, since we disallow writing the
// digest directly.
size_t digest_size = sizeof(uint64_t);
if (address >= kPartitions[partition].len - digest_size) {
return kDifOtpCtrlDaiOutOfRange;
}
uint32_t busy = mmio_region_read32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET);
if (!bitfield_bit32_read(
busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT)) {
return kDifOtpCtrlDaiBusy;
}
address += kPartitions[partition].start_addr;
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, address);
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET,
value & UINT32_MAX);
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_WDATA_1_REG_OFFSET, value >> 32);
uint32_t cmd =
bitfield_bit32_write(0, OTP_CTRL_DIRECT_ACCESS_CMD_WR_BIT, true);
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET, cmd);
return kDifOtpCtrlDaiOk;
}
dif_otp_ctrl_dai_result_t dif_otp_ctrl_dai_digest(
const dif_otp_ctrl_t *otp, dif_otp_ctrl_partition_t partition,
uint64_t digest) {
if (otp == NULL || partition >= ARRAYSIZE(kPartitions)) {
return kDifOtpCtrlDaiBadArg;
}
// The LC partition does not have a digest.
if (partition == kDifOtpCtrlPartitionLifeCycle) {
return kDifOtpCtrlDaiBadPartition;
}
// For software partitions, the digest must be nonzero; for all other
// partitions it must be zero.
bool is_sw = kPartitions[partition].is_software;
if (is_sw == (digest == 0)) {
return kDifOtpCtrlDaiBadArg;
}
uint32_t busy = mmio_region_read32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET);
if (!bitfield_bit32_read(
busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT)) {
return kDifOtpCtrlDaiBusy;
}
uint32_t address = kPartitions[partition].start_addr;
if (is_sw) {
address += kPartitions[partition].len - sizeof(digest);
}
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, address);
if (digest != 0) {
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET,
digest & 0xffffffff);
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_WDATA_1_REG_OFFSET,
digest >> 32);
}
bitfield_bit32_index_t cmd_bit = is_sw
? OTP_CTRL_DIRECT_ACCESS_CMD_WR_BIT
: OTP_CTRL_DIRECT_ACCESS_CMD_DIGEST_BIT;
uint32_t cmd = bitfield_bit32_write(0, cmd_bit, true);
mmio_region_write32(otp->params.base_addr_core,
OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET, cmd);
return kDifOtpCtrlDaiOk;
}
dif_otp_ctrl_digest_result_t dif_otp_ctrl_get_digest(
const dif_otp_ctrl_t *otp, dif_otp_ctrl_partition_t partition,
uint64_t *digest) {
if (otp == NULL || digest == NULL) {
return kDifOtpCtrlDigestBadArg;
}
// The LC partition does not have a digest.
if (partition == kDifOtpCtrlPartitionLifeCycle) {
return kDifOtpCtrlDigestBadArg;
}
ptrdiff_t reg0, reg1;
switch (partition) {
case kDifOtpCtrlPartitionCreatorSwCfg:
reg0 = OTP_CTRL_CREATOR_SW_CFG_DIGEST_0_REG_OFFSET;
reg1 = OTP_CTRL_CREATOR_SW_CFG_DIGEST_1_REG_OFFSET;
break;
case kDifOtpCtrlPartitionOwnerSwCfg:
reg0 = OTP_CTRL_OWNER_SW_CFG_DIGEST_0_REG_OFFSET;
reg1 = OTP_CTRL_OWNER_SW_CFG_DIGEST_1_REG_OFFSET;
break;
case kDifOtpCtrlPartitionHwCfg:
reg0 = OTP_CTRL_HW_CFG_DIGEST_0_REG_OFFSET;
reg1 = OTP_CTRL_HW_CFG_DIGEST_1_REG_OFFSET;
break;
case kDifOtpCtrlPartitionSecret0:
reg0 = OTP_CTRL_SECRET0_DIGEST_0_REG_OFFSET;
reg1 = OTP_CTRL_SECRET0_DIGEST_1_REG_OFFSET;
break;
case kDifOtpCtrlPartitionSecret1:
reg0 = OTP_CTRL_SECRET1_DIGEST_0_REG_OFFSET;
reg1 = OTP_CTRL_SECRET1_DIGEST_1_REG_OFFSET;
break;
case kDifOtpCtrlPartitionSecret2:
reg0 = OTP_CTRL_SECRET2_DIGEST_0_REG_OFFSET;
reg1 = OTP_CTRL_SECRET2_DIGEST_1_REG_OFFSET;
break;
default:
return kDifOtpCtrlDigestBadArg;
}
uint64_t value = mmio_region_read32(otp->params.base_addr_core, reg1);
value <<= 32;
value |= mmio_region_read32(otp->params.base_addr_core, reg0);
if (value == 0) {
return kDifOtpCtrlDigestMissing;
}
*digest = value;
return kDifOtpCtrlDigestOk;
}
dif_otp_ctrl_dai_result_t dif_otp_ctrl_read_blocking(
const dif_otp_ctrl_t *otp, dif_otp_ctrl_partition_t partition,
uint32_t address, uint32_t *buf, size_t len) {
if (otp == NULL || partition >= ARRAYSIZE(kPartitions) || buf == NULL) {
return kDifOtpCtrlDaiBadArg;
}
if (!kPartitions[partition].is_software) {
return kDifOtpCtrlDaiBadPartition;
}
if ((address & kPartitions[partition].align_mask) != 0) {
return kDifOtpCtrlDaiUnaligned;
}
if (address + len >= kPartitions[partition].len) {
return kDifOtpCtrlDaiOutOfRange;
}
ptrdiff_t reg_offset = OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET +
kPartitions[partition].start_addr + address;
mmio_region_memcpy_from_mmio32(otp->params.base_addr_core, reg_offset, buf,
len * sizeof(uint32_t));
return kDifOtpCtrlDaiOk;
}
dif_otp_ctrl_result_t dif_otp_ctrl_read_test(const dif_otp_ctrl_t *otp,
uint32_t address, uint32_t *buf,
size_t len) {
if (otp == NULL || buf == NULL) {
return kDifOtpCtrlBadArg;
}
mmio_region_memcpy_from_mmio32(otp->params.base_addr_prim, address, buf,
len * sizeof(uint32_t));
return kDifOtpCtrlOk;
}
dif_otp_ctrl_result_t dif_otp_ctrl_write_test(const dif_otp_ctrl_t *otp,
uint32_t address,
const uint32_t *buf, size_t len) {
if (otp == NULL || buf == NULL) {
return kDifOtpCtrlBadArg;
}
mmio_region_memcpy_to_mmio32(otp->params.base_addr_prim, address, buf,
len * sizeof(uint32_t));
return kDifOtpCtrlOk;
}