Software APIs
otp.c
1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5#include "sw/device/silicon_creator/lib/drivers/otp.h"
6
7#include <stddef.h>
8
9#include "hw/top/dt/otp_ctrl.h"
14#include "sw/device/silicon_creator/lib/error.h"
15
16#include "hw/top/otp_ctrl_regs.h" // Generated.
17
18static inline uint32_t otp_ctrl_base(void) {
19 return dt_otp_ctrl_primary_reg_block(kDtOtpCtrl);
20}
21
22uint32_t otp_read32(uint32_t address) {
23 return sec_mmio_read32(otp_ctrl_base() + OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET +
24 address);
25}
26
27uint64_t otp_read64(uint32_t address) {
28 uint32_t reg_offset = OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET + address;
29 uint64_t value =
30 sec_mmio_read32(otp_ctrl_base() + reg_offset + sizeof(uint32_t));
31 value <<= 32;
32 value |= sec_mmio_read32(otp_ctrl_base() + reg_offset);
33
34 return value;
35}
36
37void otp_read(uint32_t address, uint32_t *data, size_t num_words) {
38 uint32_t reg_offset = OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET + address;
39 size_t i = 0, r = num_words - 1;
40 for (; launder32(i) < num_words && launder32(r) < num_words; ++i, --r) {
41 data[i] =
42 sec_mmio_read32(otp_ctrl_base() + reg_offset + i * sizeof(uint32_t));
43 }
44 HARDENED_CHECK_EQ(i, num_words);
45 HARDENED_CHECK_EQ(r, SIZE_MAX);
46}
47
48dt_otp_partition_info_t otp_readable_partition_info(otp_partition_t partition) {
49 HARDENED_CHECK_LT(partition, kOtpPartitionCount);
52 HARDENED_CHECK_GT(partition_info.size, 0);
53
54 return partition_info;
55}
56
57uint64_t otp_partition_digest_read(otp_partition_t partition) {
58 uint32_t reg_offset =
59 otp_ctrl_base() +
60 otp_readable_partition_info(partition).digest_reg_offset;
61 uint64_t value = sec_mmio_read32(reg_offset + sizeof(uint32_t));
62 value <<= 32;
63 value |= sec_mmio_read32(reg_offset);
64 return value;
65}
66
67static void wait_for_dai_idle(void) {
68 uint32_t status = 0;
69 bool idle = false;
70 do {
71 status = abs_mmio_read32(otp_ctrl_base() + OTP_CTRL_STATUS_REG_OFFSET);
72 idle = bitfield_bit32_read(status, OTP_CTRL_STATUS_DAI_IDLE_BIT);
73 } while (!idle);
74}
75
76static void dai_read_blocking(otp_partition_t partition,
77 uint32_t relative_address) {
78 wait_for_dai_idle();
80 relative_address & otp_readable_partition_info(partition).align_mask, 0);
81 abs_mmio_write32(
82 otp_ctrl_base() + OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET,
83 otp_readable_partition_info(partition).start_addr + relative_address);
84 uint32_t cmd =
85 bitfield_bit32_write(0, OTP_CTRL_DIRECT_ACCESS_CMD_RD_BIT, true);
86 abs_mmio_write32(otp_ctrl_base() + OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET,
87 cmd);
88 wait_for_dai_idle();
89}
90
91uint32_t otp_dai_read32(otp_partition_t partition, uint32_t relative_address) {
92 dai_read_blocking(partition, relative_address);
93 return abs_mmio_read32(otp_ctrl_base() +
94 OTP_CTRL_DIRECT_ACCESS_RDATA_0_REG_OFFSET);
95}
96
97uint64_t otp_dai_read64(otp_partition_t partition, uint32_t relative_address) {
98 dai_read_blocking(partition, relative_address);
99 uint64_t value = abs_mmio_read32(otp_ctrl_base() +
100 OTP_CTRL_DIRECT_ACCESS_RDATA_1_REG_OFFSET);
101 value <<= 32;
102 value |= abs_mmio_read32(otp_ctrl_base() +
103 OTP_CTRL_DIRECT_ACCESS_RDATA_0_REG_OFFSET);
104 return value;
105}
106
107rom_error_t otp_dai_read(otp_partition_t partition, uint32_t relative_address,
108 uint32_t *data, size_t num_words) {
109 size_t i = 0, r = num_words - 1;
110 uint32_t addr = relative_address;
111 for (; launder32(i) < num_words && launder32(r) < num_words; ++i, --r) {
112 data[i] = otp_dai_read32(partition, addr);
113 addr += sizeof(uint32_t);
114 }
115 HARDENED_CHECK_EQ(i, num_words);
116 HARDENED_CHECK_EQ(r, SIZE_MAX);
117 return kErrorOk;
118}
119
120void otp_creator_sw_cfg_lockdown(void) {
121 SEC_MMIO_ASSERT_WRITE_INCREMENT(kOtpSecMmioCreatorSwCfgLockDown, 1);
123 otp_ctrl_base() + OTP_CTRL_CREATOR_SW_CFG_READ_LOCK_REG_OFFSET, 0);
124}