| // 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/macros.h" | 
 | #include "sw/device/lib/dif/dif_base.h" | 
 |  | 
 | #include "otp_ctrl_regs.h"  // Generated. | 
 |  | 
 | /** | 
 |  * 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->base_addr, OTP_CTRL_CHECK_REGWEN_REG_OFFSET); | 
 |   return !bitfield_bit32_read(locked, OTP_CTRL_CHECK_REGWEN_CHECK_REGWEN_BIT); | 
 | } | 
 |  | 
 | dif_result_t dif_otp_ctrl_configure(const dif_otp_ctrl_t *otp, | 
 |                                     dif_otp_ctrl_config_t config) { | 
 |   if (otp == NULL) { | 
 |     return kDifBadArg; | 
 |   } | 
 |   if (checks_are_locked(otp)) { | 
 |     return kDifLocked; | 
 |   } | 
 |  | 
 |   mmio_region_write32(otp->base_addr, OTP_CTRL_CHECK_TIMEOUT_REG_OFFSET, | 
 |                       config.check_timeout); | 
 |   mmio_region_write32(otp->base_addr, | 
 |                       OTP_CTRL_INTEGRITY_CHECK_PERIOD_REG_OFFSET, | 
 |                       config.integrity_period_mask); | 
 |   mmio_region_write32(otp->base_addr, | 
 |                       OTP_CTRL_CONSISTENCY_CHECK_PERIOD_REG_OFFSET, | 
 |                       config.consistency_period_mask); | 
 |  | 
 |   return kDifOk; | 
 | } | 
 |  | 
 | dif_result_t dif_otp_ctrl_check_integrity(const dif_otp_ctrl_t *otp) { | 
 |   if (otp == NULL) { | 
 |     return kDifBadArg; | 
 |   } | 
 |   if (checks_are_locked(otp)) { | 
 |     return kDifLocked; | 
 |   } | 
 |  | 
 |   uint32_t reg = | 
 |       bitfield_bit32_write(0, OTP_CTRL_CHECK_TRIGGER_INTEGRITY_BIT, true); | 
 |   mmio_region_write32(otp->base_addr, OTP_CTRL_CHECK_TRIGGER_REG_OFFSET, reg); | 
 |  | 
 |   return kDifOk; | 
 | } | 
 |  | 
 | dif_result_t dif_otp_ctrl_check_consistency(const dif_otp_ctrl_t *otp) { | 
 |   if (otp == NULL) { | 
 |     return kDifBadArg; | 
 |   } | 
 |   if (checks_are_locked(otp)) { | 
 |     return kDifLocked; | 
 |   } | 
 |  | 
 |   uint32_t reg = | 
 |       bitfield_bit32_write(0, OTP_CTRL_CHECK_TRIGGER_CONSISTENCY_BIT, true); | 
 |   mmio_region_write32(otp->base_addr, OTP_CTRL_CHECK_TRIGGER_REG_OFFSET, reg); | 
 |  | 
 |   return kDifOk; | 
 | } | 
 |  | 
 | dif_result_t dif_otp_ctrl_lock_config(const dif_otp_ctrl_t *otp) { | 
 |   if (otp == NULL) { | 
 |     return kDifBadArg; | 
 |   } | 
 |  | 
 |   uint32_t reg = | 
 |       bitfield_bit32_write(0, OTP_CTRL_CHECK_REGWEN_CHECK_REGWEN_BIT, true); | 
 |   mmio_region_write32(otp->base_addr, OTP_CTRL_CHECK_REGWEN_REG_OFFSET, reg); | 
 |  | 
 |   return kDifOk; | 
 | } | 
 |  | 
 | dif_result_t dif_otp_ctrl_config_is_locked(const dif_otp_ctrl_t *otp, | 
 |                                            bool *is_locked) { | 
 |   if (otp == NULL || is_locked == NULL) { | 
 |     return kDifBadArg; | 
 |   } | 
 |  | 
 |   *is_locked = checks_are_locked(otp); | 
 |   return kDifOk; | 
 | } | 
 |  | 
 | 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 kDifOtpCtrlPartitionVendorTest: | 
 |       *reg_offset = OTP_CTRL_VENDOR_TEST_READ_LOCK_REG_OFFSET; | 
 |       *index = OTP_CTRL_VENDOR_TEST_READ_LOCK_VENDOR_TEST_READ_LOCK_BIT; | 
 |       break; | 
 |     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_result_t dif_otp_ctrl_lock_reading(const dif_otp_ctrl_t *otp, | 
 |                                        dif_otp_ctrl_partition_t partition) { | 
 |   if (otp == NULL) { | 
 |     return kDifBadArg; | 
 |   } | 
 |  | 
 |   ptrdiff_t offset; | 
 |   bitfield_bit32_index_t index; | 
 |   if (!sw_read_lock_reg_offset(partition, &offset, &index)) { | 
 |     return kDifBadArg; | 
 |   } | 
 |  | 
 |   uint32_t reg = bitfield_bit32_write(0, index, true); | 
 |   mmio_region_write32(otp->base_addr, offset, reg); | 
 |  | 
 |   return kDifOk; | 
 | } | 
 |  | 
 | dif_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 kDifBadArg; | 
 |   } | 
 |  | 
 |   ptrdiff_t offset; | 
 |   bitfield_bit32_index_t index; | 
 |   if (!sw_read_lock_reg_offset(partition, &offset, &index)) { | 
 |     return kDifBadArg; | 
 |   } | 
 |  | 
 |   uint32_t reg = mmio_region_read32(otp->base_addr, offset); | 
 |   *is_locked = !bitfield_bit32_read(reg, index); | 
 |   return kDifOk; | 
 | } | 
 |  | 
 | dif_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 kDifBadArg; | 
 |   } | 
 |  | 
 |   static const bitfield_bit32_index_t kIndices[] = { | 
 |       [kDifOtpCtrlStatusCodeVendorTestError] = | 
 |           OTP_CTRL_STATUS_VENDOR_TEST_ERROR_BIT, | 
 |       [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->base_addr, OTP_CTRL_STATUS_REG_OFFSET); | 
 |   uint32_t error_codes = | 
 |       mmio_region_read32(otp->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 kDifOtpCtrlStatusCodeVendorTestError: | 
 |         field = (bitfield_field32_t){ | 
 |             .mask = OTP_CTRL_ERR_CODE_ERR_CODE_0_MASK, | 
 |             .index = OTP_CTRL_ERR_CODE_ERR_CODE_0_OFFSET, | 
 |         }; | 
 |         break; | 
 |       case kDifOtpCtrlStatusCodeCreatorSwCfgError: | 
 |         field = (bitfield_field32_t){ | 
 |             .mask = OTP_CTRL_ERR_CODE_ERR_CODE_1_MASK, | 
 |             .index = OTP_CTRL_ERR_CODE_ERR_CODE_1_OFFSET, | 
 |         }; | 
 |         break; | 
 |       case kDifOtpCtrlStatusCodeOwnerSwCfgError: | 
 |         field = (bitfield_field32_t){ | 
 |             .mask = OTP_CTRL_ERR_CODE_ERR_CODE_2_MASK, | 
 |             .index = OTP_CTRL_ERR_CODE_ERR_CODE_2_OFFSET, | 
 |         }; | 
 |         break; | 
 |       case kDifOtpCtrlStatusCodeHwCfgError: | 
 |         field = (bitfield_field32_t){ | 
 |             .mask = OTP_CTRL_ERR_CODE_ERR_CODE_3_MASK, | 
 |             .index = OTP_CTRL_ERR_CODE_ERR_CODE_3_OFFSET, | 
 |         }; | 
 |         break; | 
 |       case kDifOtpCtrlStatusCodeSecret0Error: | 
 |         field = (bitfield_field32_t){ | 
 |             .mask = OTP_CTRL_ERR_CODE_ERR_CODE_4_MASK, | 
 |             .index = OTP_CTRL_ERR_CODE_ERR_CODE_4_OFFSET, | 
 |         }; | 
 |         break; | 
 |       case kDifOtpCtrlStatusCodeSecret1Error: | 
 |         field = (bitfield_field32_t){ | 
 |             .mask = OTP_CTRL_ERR_CODE_ERR_CODE_5_MASK, | 
 |             .index = OTP_CTRL_ERR_CODE_ERR_CODE_5_OFFSET, | 
 |         }; | 
 |         break; | 
 |       case kDifOtpCtrlStatusCodeSecret2Error: | 
 |         field = (bitfield_field32_t){ | 
 |             .mask = OTP_CTRL_ERR_CODE_ERR_CODE_6_MASK, | 
 |             .index = OTP_CTRL_ERR_CODE_ERR_CODE_6_OFFSET, | 
 |         }; | 
 |         break; | 
 |       case kDifOtpCtrlStatusCodeLifeCycleError: | 
 |         field = (bitfield_field32_t){ | 
 |             .mask = OTP_CTRL_ERR_CODE_ERR_CODE_7_MASK, | 
 |             .index = OTP_CTRL_ERR_CODE_ERR_CODE_7_OFFSET, | 
 |         }; | 
 |         break; | 
 |       case kDifOtpCtrlStatusCodeDaiError: | 
 |         field = (bitfield_field32_t){ | 
 |             .mask = OTP_CTRL_ERR_CODE_ERR_CODE_8_MASK, | 
 |             .index = OTP_CTRL_ERR_CODE_ERR_CODE_8_OFFSET, | 
 |         }; | 
 |         break; | 
 |       case kDifOtpCtrlStatusCodeLciError: | 
 |         field = (bitfield_field32_t){ | 
 |             .mask = OTP_CTRL_ERR_CODE_ERR_CODE_9_MASK, | 
 |             .index = OTP_CTRL_ERR_CODE_ERR_CODE_9_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 kDifError; | 
 |     } | 
 |     status->causes[i] = err; | 
 |   } | 
 |  | 
 |   return kDifOk; | 
 | } | 
 |  | 
 | 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. | 
 |     [kDifOtpCtrlPartitionVendorTest] = | 
 |         { | 
 |             .start_addr = OTP_CTRL_PARAM_VENDOR_TEST_OFFSET, | 
 |             .len = OTP_CTRL_PARAM_VENDOR_TEST_SIZE, | 
 |             .align_mask = 0x3, | 
 |             .is_software = true, | 
 |         }, | 
 |     [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_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 kDifBadArg; | 
 |   } | 
 |  | 
 |   if ((address & kPartitions[partition].align_mask) != 0) { | 
 |     return kDifUnaligned; | 
 |   } | 
 |  | 
 |   if (address >= kPartitions[partition].len) { | 
 |     return kDifOutOfRange; | 
 |   } | 
 |  | 
 |   uint32_t busy = mmio_region_read32(otp->base_addr, | 
 |                                      OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET); | 
 |   if (!bitfield_bit32_read( | 
 |           busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT)) { | 
 |     return kDifUnavailable; | 
 |   } | 
 |  | 
 |   address += kPartitions[partition].start_addr; | 
 |   mmio_region_write32(otp->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->base_addr, OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET, | 
 |                       cmd); | 
 |  | 
 |   return kDifOk; | 
 | } | 
 |  | 
 | dif_result_t dif_otp_ctrl_dai_read32_end(const dif_otp_ctrl_t *otp, | 
 |                                          uint32_t *value) { | 
 |   if (otp == NULL || value == NULL) { | 
 |     return kDifBadArg; | 
 |   } | 
 |  | 
 |   uint32_t busy = mmio_region_read32(otp->base_addr, | 
 |                                      OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET); | 
 |   if (!bitfield_bit32_read( | 
 |           busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT)) { | 
 |     return kDifUnavailable; | 
 |   } | 
 |  | 
 |   *value = mmio_region_read32(otp->base_addr, | 
 |                               OTP_CTRL_DIRECT_ACCESS_RDATA_0_REG_OFFSET); | 
 |   return kDifOk; | 
 | } | 
 |  | 
 | dif_result_t dif_otp_ctrl_dai_read64_end(const dif_otp_ctrl_t *otp, | 
 |                                          uint64_t *value) { | 
 |   if (otp == NULL || value == NULL) { | 
 |     return kDifBadArg; | 
 |   } | 
 |  | 
 |   uint32_t busy = mmio_region_read32(otp->base_addr, | 
 |                                      OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET); | 
 |   if (!bitfield_bit32_read( | 
 |           busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT)) { | 
 |     return kDifUnavailable; | 
 |   } | 
 |  | 
 |   *value = mmio_region_read32(otp->base_addr, | 
 |                               OTP_CTRL_DIRECT_ACCESS_RDATA_1_REG_OFFSET); | 
 |   *value <<= 32; | 
 |   *value |= mmio_region_read32(otp->base_addr, | 
 |                                OTP_CTRL_DIRECT_ACCESS_RDATA_0_REG_OFFSET); | 
 |   return kDifOk; | 
 | } | 
 |  | 
 | dif_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 kDifBadArg; | 
 |   } | 
 |  | 
 |   // 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 kDifError; | 
 |   } | 
 |  | 
 |   if ((address & kPartitions[partition].align_mask) != 0) { | 
 |     return kDifUnaligned; | 
 |   } | 
 |  | 
 |   // 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 kDifOutOfRange; | 
 |   } | 
 |  | 
 |   uint32_t busy = mmio_region_read32(otp->base_addr, | 
 |                                      OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET); | 
 |   if (!bitfield_bit32_read( | 
 |           busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT)) { | 
 |     return kDifUnavailable; | 
 |   } | 
 |  | 
 |   address += kPartitions[partition].start_addr; | 
 |   mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, | 
 |                       address); | 
 |  | 
 |   mmio_region_write32(otp->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->base_addr, OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET, | 
 |                       cmd); | 
 |  | 
 |   return kDifOk; | 
 | } | 
 |  | 
 | dif_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 kDifBadArg; | 
 |   } | 
 |  | 
 |   // Ensure that we are writing to a 64-bit-access partition by checking that | 
 |   // the alignment mask is 0b111. | 
 |   if (kPartitions[partition].align_mask != 0x7) { | 
 |     return kDifError; | 
 |   } | 
 |  | 
 |   if ((address & kPartitions[partition].align_mask) != 0) { | 
 |     return kDifUnaligned; | 
 |   } | 
 |  | 
 |   // 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 kDifOutOfRange; | 
 |   } | 
 |  | 
 |   uint32_t busy = mmio_region_read32(otp->base_addr, | 
 |                                      OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET); | 
 |   if (!bitfield_bit32_read( | 
 |           busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT)) { | 
 |     return kDifUnavailable; | 
 |   } | 
 |  | 
 |   address += kPartitions[partition].start_addr; | 
 |   mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, | 
 |                       address); | 
 |  | 
 |   mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET, | 
 |                       value & UINT32_MAX); | 
 |   mmio_region_write32(otp->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->base_addr, OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET, | 
 |                       cmd); | 
 |  | 
 |   return kDifOk; | 
 | } | 
 |  | 
 | dif_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 kDifBadArg; | 
 |   } | 
 |  | 
 |   // The LC partition does not have a digest. | 
 |   if (partition == kDifOtpCtrlPartitionLifeCycle) { | 
 |     return kDifError; | 
 |   } | 
 |  | 
 |   // 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 kDifBadArg; | 
 |   } | 
 |  | 
 |   uint32_t busy = mmio_region_read32(otp->base_addr, | 
 |                                      OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET); | 
 |   if (!bitfield_bit32_read( | 
 |           busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT)) { | 
 |     return kDifUnavailable; | 
 |   } | 
 |  | 
 |   uint32_t address = kPartitions[partition].start_addr; | 
 |   if (is_sw) { | 
 |     address += kPartitions[partition].len - sizeof(digest); | 
 |   } | 
 |   mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, | 
 |                       address); | 
 |  | 
 |   if (digest != 0) { | 
 |     mmio_region_write32(otp->base_addr, | 
 |                         OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET, | 
 |                         digest & 0xffffffff); | 
 |     mmio_region_write32(otp->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->base_addr, OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET, | 
 |                       cmd); | 
 |  | 
 |   return kDifOk; | 
 | } | 
 |  | 
 | dif_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 kDifBadArg; | 
 |   } | 
 |  | 
 |   // The LC partition does not have a digest. | 
 |   if (partition == kDifOtpCtrlPartitionLifeCycle) { | 
 |     return kDifBadArg; | 
 |   } | 
 |  | 
 |   ptrdiff_t reg0, reg1; | 
 |   switch (partition) { | 
 |     case kDifOtpCtrlPartitionVendorTest: | 
 |       reg0 = OTP_CTRL_VENDOR_TEST_DIGEST_0_REG_OFFSET; | 
 |       reg1 = OTP_CTRL_VENDOR_TEST_DIGEST_1_REG_OFFSET; | 
 |       break; | 
 |     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 kDifBadArg; | 
 |   } | 
 |  | 
 |   uint64_t value = mmio_region_read32(otp->base_addr, reg1); | 
 |   value <<= 32; | 
 |   value |= mmio_region_read32(otp->base_addr, reg0); | 
 |  | 
 |   if (value == 0) { | 
 |     return kDifError; | 
 |   } | 
 |   *digest = value; | 
 |  | 
 |   return kDifOk; | 
 | } | 
 |  | 
 | dif_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 kDifBadArg; | 
 |   } | 
 |  | 
 |   if (!kPartitions[partition].is_software) { | 
 |     return kDifError; | 
 |   } | 
 |  | 
 |   if ((address & kPartitions[partition].align_mask) != 0) { | 
 |     return kDifUnaligned; | 
 |   } | 
 |  | 
 |   if (address + len >= kPartitions[partition].len) { | 
 |     return kDifOutOfRange; | 
 |   } | 
 |  | 
 |   ptrdiff_t reg_offset = OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET + | 
 |                          kPartitions[partition].start_addr + address; | 
 |   mmio_region_memcpy_from_mmio32(otp->base_addr, reg_offset, buf, | 
 |                                  len * sizeof(uint32_t)); | 
 |   return kDifOk; | 
 | } |