[dif/otp_ctrl] Implement DIFs for otp_ctrl
Signed-off-by: Miguel Young de la Sota <mcyoung@google.com>
diff --git a/sw/device/lib/dif/dif_otp_ctrl.c b/sw/device/lib/dif/dif_otp_ctrl.c
index 2c8df6a..5e7151e 100644
--- a/sw/device/lib/dif/dif_otp_ctrl.c
+++ b/sw/device/lib/dif/dif_otp_ctrl.c
@@ -3,3 +3,856 @@
// 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,
+ OTP_CTRL_CHECK_REGWEN_REG_OFFSET);
+ return !bitfield_bit32_read(locked, OTP_CTRL_CHECK_REGWEN_CHECK_REGWEN);
+}
+
+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, OTP_CTRL_CHECK_TIMEOUT_REG_OFFSET,
+ config.check_timeout);
+ mmio_region_write32(otp->params.base_addr,
+ OTP_CTRL_INTEGRITY_CHECK_PERIOD_REG_OFFSET,
+ config.integrity_period_mask);
+ mmio_region_write32(otp->params.base_addr,
+ 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, true);
+ mmio_region_write32(otp->params.base_addr, 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, true);
+ mmio_region_write32(otp->params.base_addr, 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, true);
+ mmio_region_write32(otp->params.base_addr, 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;
+ 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;
+ 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, 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, 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;
+ break;
+ case kDifOtpCtrlIrqError:
+ *index = OTP_CTRL_INTR_COMMON_OTP_ERROR;
+ 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, 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, 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,
+ 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,
+ OTP_CTRL_INTR_ENABLE_REG_OFFSET);
+ reg = bitfield_bit32_write(reg, index, flag);
+ mmio_region_write32(otp->params.base_addr, 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, 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,
+ OTP_CTRL_INTR_ENABLE_REG_OFFSET);
+ }
+
+ mmio_region_write32(otp->params.base_addr, 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, 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,
+ [kDifOtpCtrlStatusCodeOwnerSwCfgError] =
+ OTP_CTRL_STATUS_OWNER_SW_CFG_ERROR,
+ [kDifOtpCtrlStatusCodeHwCfgError] = OTP_CTRL_STATUS_HW_CFG_ERROR,
+ [kDifOtpCtrlStatusCodeLifeCycleError] = OTP_CTRL_STATUS_LIFE_CYCLE_ERROR,
+ [kDifOtpCtrlStatusCodeSecret0Error] = OTP_CTRL_STATUS_SECRET0_ERROR,
+ [kDifOtpCtrlStatusCodeSecret1Error] = OTP_CTRL_STATUS_SECRET1_ERROR,
+ [kDifOtpCtrlStatusCodeSecret2Error] = OTP_CTRL_STATUS_SECRET2_ERROR,
+ [kDifOtpCtrlStatusCodeDaiError] = OTP_CTRL_STATUS_DAI_ERROR,
+ [kDifOtpCtrlStatusCodeLciError] = OTP_CTRL_STATUS_LCI_ERROR,
+ [kDifOtpCtrlStatusCodeTimeoutError] = OTP_CTRL_STATUS_TIMEOUT_ERROR,
+ [kDifOtpCtrlStatusCodeLfsrError] = OTP_CTRL_STATUS_LFSR_FSM_ERROR,
+ [kDifOtpCtrlStatusCodeScramblingError] =
+ OTP_CTRL_STATUS_SCRAMBLING_FSM_ERROR,
+ [kDifOtpCtrlStatusCodeKdfError] = OTP_CTRL_STATUS_KEY_DERIV_FSM_ERROR,
+ [kDifOtpCtrlStatusCodeDaiIdle] = OTP_CTRL_STATUS_DAI_IDLE,
+ [kDifOtpCtrlStatusCodeCheckPending] = OTP_CTRL_STATUS_CHECK_PENDING,
+ };
+
+ status->codes = 0;
+ uint32_t status_code =
+ mmio_region_read32(otp->params.base_addr, OTP_CTRL_STATUS_REG_OFFSET);
+ uint32_t error_codes =
+ mmio_region_read32(otp->params.base_addr, 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.
+ [kDifOtpCtrlPartitionCreatorSwCfg] =
+ {
+ .start_addr = 0,
+ .len = 0x300,
+ .align_mask = 0x3,
+ .is_software = true,
+ },
+ [kDifOtpCtrlPartitionOwnerSwCfg] =
+ {
+ .start_addr = 0x300,
+ .len = 0x300,
+ .align_mask = 0x3,
+ .is_software = true,
+ },
+ [kDifOtpCtrlPartitionHwCfg] =
+ {
+ .start_addr = 0x600,
+ .len = 0xb0,
+ .align_mask = 0x3,
+ },
+ [kDifOtpCtrlPartitionSecret0] =
+ {
+ .start_addr = 0x6b0,
+ .len = 0x28,
+ .align_mask = 0x7,
+ },
+ [kDifOtpCtrlPartitionSecret1] =
+ {
+ .start_addr = 0x6d8,
+ .len = 0x58,
+ .align_mask = 0x7,
+ },
+ [kDifOtpCtrlPartitionSecret2] =
+ {
+ .start_addr = 0x730,
+ .len = 0x58,
+ .align_mask = 0x7,
+ },
+ [kDifOtpCtrlPartitionLifeCycle] =
+ {
+ .start_addr = 0x7a8,
+ .len = 0x58,
+ .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,
+ OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET);
+ if (!bitfield_bit32_read(
+ busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN)) {
+ return kDifOtpCtrlDaiBusy;
+ }
+
+ address += kPartitions[partition].start_addr;
+ mmio_region_write32(otp->params.base_addr,
+ OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, address);
+
+ uint32_t cmd = bitfield_bit32_write(0, OTP_CTRL_DIRECT_ACCESS_CMD_READ, true);
+ mmio_region_write32(otp->params.base_addr,
+ 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,
+ OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET);
+ if (!bitfield_bit32_read(
+ busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN)) {
+ return kDifOtpCtrlDaiBusy;
+ }
+
+ *value = mmio_region_read32(otp->params.base_addr,
+ 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,
+ OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET);
+ if (!bitfield_bit32_read(
+ busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN)) {
+ return kDifOtpCtrlDaiBusy;
+ }
+
+ *value = mmio_region_read32(otp->params.base_addr,
+ OTP_CTRL_DIRECT_ACCESS_RDATA_1_REG_OFFSET);
+ *value <<= 32;
+ *value |= mmio_region_read32(otp->params.base_addr,
+ 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,
+ OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET);
+ if (!bitfield_bit32_read(
+ busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN)) {
+ return kDifOtpCtrlDaiBusy;
+ }
+
+ address += kPartitions[partition].start_addr;
+ mmio_region_write32(otp->params.base_addr,
+ OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, address);
+
+ mmio_region_write32(otp->params.base_addr,
+ OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET, value);
+
+ uint32_t cmd =
+ bitfield_bit32_write(0, OTP_CTRL_DIRECT_ACCESS_CMD_WRITE, true);
+ mmio_region_write32(otp->params.base_addr,
+ 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,
+ OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET);
+ if (!bitfield_bit32_read(
+ busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN)) {
+ return kDifOtpCtrlDaiBusy;
+ }
+
+ address += kPartitions[partition].start_addr;
+ mmio_region_write32(otp->params.base_addr,
+ OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, address);
+
+ mmio_region_write32(otp->params.base_addr,
+ OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET,
+ value & UINT32_MAX);
+ mmio_region_write32(otp->params.base_addr,
+ OTP_CTRL_DIRECT_ACCESS_WDATA_1_REG_OFFSET, value >> 32);
+
+ uint32_t cmd =
+ bitfield_bit32_write(0, OTP_CTRL_DIRECT_ACCESS_CMD_WRITE, true);
+ mmio_region_write32(otp->params.base_addr,
+ 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,
+ OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET);
+ if (!bitfield_bit32_read(
+ busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN)) {
+ 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,
+ OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, address);
+
+ if (digest != 0) {
+ mmio_region_write32(otp->params.base_addr,
+ OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET,
+ digest & 0xffffffff);
+ mmio_region_write32(otp->params.base_addr,
+ OTP_CTRL_DIRECT_ACCESS_WDATA_1_REG_OFFSET,
+ digest >> 32);
+ }
+
+ bitfield_bit32_index_t cmd_bit = is_sw ? OTP_CTRL_DIRECT_ACCESS_CMD_WRITE
+ : OTP_CTRL_DIRECT_ACCESS_CMD_DIGEST;
+ uint32_t cmd = bitfield_bit32_write(0, cmd_bit, true);
+ mmio_region_write32(otp->params.base_addr,
+ 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, reg1);
+ value <<= 32;
+ value |= mmio_region_read32(otp->params.base_addr, 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, 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;
+ }
+
+ ptrdiff_t reg_offset = OTP_CTRL_TEST_ACCESS_REG_OFFSET + address;
+ mmio_region_memcpy_from_mmio32(otp->params.base_addr, reg_offset, 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;
+ }
+
+ ptrdiff_t reg_offset = OTP_CTRL_TEST_ACCESS_REG_OFFSET + address;
+ mmio_region_memcpy_to_mmio32(otp->params.base_addr, reg_offset, buf,
+ len * sizeof(uint32_t));
+ return kDifOtpCtrlOk;
+}
diff --git a/sw/device/lib/dif/dif_otp_ctrl.h b/sw/device/lib/dif/dif_otp_ctrl.h
index 9a8cb57..d59d48d 100644
--- a/sw/device/lib/dif/dif_otp_ctrl.h
+++ b/sw/device/lib/dif/dif_otp_ctrl.h
@@ -125,8 +125,7 @@
* The value of this mask limits the period of the consistency check; when the
* pseudo-random period is computed, this mask is applied to limit it. For
* example, a value of 0x3ff'ffff would correspond to a maximum period of
- * about
- * 716s at 24MHz.
+ * about 716s at 24MHz.
*
* A value of zero disables the check.
*/
@@ -503,7 +502,8 @@
const dif_otp_ctrl_t *otp);
/**
- * Locks out `dif_otp_ctrl_configure()`.
+ * Locks out `dif_otp_ctrl_configure()` and the
+ * `dif_otp_ctrl_check_*()` functions.
*
* This function is reentrant: calling it while functionality is locked will
* have no effect and return `kDifOtpCtrlOk`.
@@ -515,7 +515,8 @@
dif_otp_ctrl_result_t dif_otp_ctrl_lock_config(const dif_otp_ctrl_t *otp);
/**
- * Checks whether `dif_otp_ctrl_configure()` is locked-out.
+ * Checks whether `dif_otp_ctrl_configure()` and the `dif_otp_ctrl_check_*()`
+ * functions are locked-out.
*
* @param otp An OTP handle.
* @param[out] is_locked Out-param for the locked state.
@@ -526,29 +527,6 @@
bool *is_locked);
/**
- * Locks out `dif_otp_ctrl_check_*()` functions.
- *
- * This function is reentrant: calling it while functionality is locked will
- * have no effect and return `kDifOtpCtrlOk`.
- *
- * @param otp An OTP handle.
- * @return The result of the operation.
- */
-DIF_WARN_UNUSED_RESULT
-dif_otp_ctrl_result_t dif_otp_ctrl_lock_checking(const dif_otp_ctrl_t *otp);
-
-/**
- * Checks whether `dif_otp_ctrl_check_*()` functions are locked-out.
- *
- * @param otp An OTP handle.
- * @param[out] is_locked Out-param for the locked state.
- * @return The result of the operation.
- */
-DIF_WARN_UNUSED_RESULT
-dif_otp_ctrl_result_t dif_otp_ctrl_checking_is_locked(const dif_otp_ctrl_t *otp,
- bool *is_locked);
-
-/**
* Locks out reads to a SW partition.
*
* This function should only be called on SW partitions; doing otherwise will
@@ -686,18 +664,6 @@
dif_otp_ctrl_status_t *status);
/**
- * Checks whether the Direct Access Interface is busy, such that no commands
- * can be scheduled at this time.
- *
- * @param otp An OTP handle.
- * @param[out] is_busy Out-param for whether the DAI is busy.
- * @return The result of the operation.
- */
-DIF_WARN_UNUSED_RESULT
-dif_otp_ctrl_result_t dif_otp_ctrl_dai_is_busy(const dif_otp_ctrl_t *otp,
- bool *is_busy);
-
-/**
* Schedules a read on the Direct Access Interface.
*
* Reads are performed relative to a partition; `address` should be given
@@ -730,8 +696,8 @@
* @return The result of the operation.
*/
DIF_WARN_UNUSED_RESULT
-dif_otp_ctrl_result_t dif_otp_ctrl_dai_read32_end(const dif_otp_ctrl_t *otp,
- uint32_t *value);
+dif_otp_ctrl_dai_result_t dif_otp_ctrl_dai_read32_end(const dif_otp_ctrl_t *otp,
+ uint32_t *value);
/**
* Gets the result of a completed 64-bit read operation on the Direct Access
@@ -745,8 +711,8 @@
* @return The result of the operation.
*/
DIF_WARN_UNUSED_RESULT
-dif_otp_ctrl_result_t dif_otp_ctrl_dai_read64_end(const dif_otp_ctrl_t *otp,
- uint64_t *value);
+dif_otp_ctrl_dai_result_t dif_otp_ctrl_dai_read64_end(const dif_otp_ctrl_t *otp,
+ uint64_t *value);
/**
* Schedules a 32-bit write on the Direct Access Interface.