|  | // 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, | 
|  | 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, 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_BIT, 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_BIT, 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_BIT, 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_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, 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_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, 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_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, | 
|  | [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, 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. | 
|  | // See #3904. | 
|  | [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_BIT)) { | 
|  | 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_RD_BIT, 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_BIT)) { | 
|  | 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_BIT)) { | 
|  | 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_BIT)) { | 
|  | 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_WR_BIT, 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_BIT)) { | 
|  | 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_WR_BIT, 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_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, | 
|  | 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_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, | 
|  | 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; | 
|  | } |