blob: e17658788e167c6157ca90adc35d64a4b5d8e28a [file] [log] [blame]
Miguel Young de la Sota1fac7822020-09-18 13:47:28 -04001// Copyright lowRISC contributors.
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/lib/dif/dif_otp_ctrl.h"
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -04006
7#include <stddef.h>
8
9#include "sw/device/lib/base/bitfield.h"
10#include "sw/device/lib/base/memory.h"
11
12#include "otp_ctrl_regs.h" // Generated.
13
14dif_otp_ctrl_result_t dif_otp_ctrl_init(dif_otp_ctrl_params_t params,
15 dif_otp_ctrl_t *otp) {
16 if (otp == NULL) {
17 return kDifOtpCtrlBadArg;
18 }
19
20 otp->params = params;
21 return kDifOtpCtrlOk;
22}
23
24/**
25 * Checks if integrity/consistency-check-related operations are locked.
26 *
27 * This is a convenience function to avoid superfluous error-checking in all the
28 * functions that can be locked out by this register.
29 */
30static bool checks_are_locked(const dif_otp_ctrl_t *otp) {
31 uint32_t locked = mmio_region_read32(otp->params.base_addr,
32 OTP_CTRL_CHECK_REGWEN_REG_OFFSET);
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -050033 return !bitfield_bit32_read(locked, OTP_CTRL_CHECK_REGWEN_CHECK_REGWEN_BIT);
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -040034}
35
36dif_otp_ctrl_lockable_result_t dif_otp_ctrl_configure(
37 const dif_otp_ctrl_t *otp, dif_otp_ctrl_config_t config) {
38 if (otp == NULL) {
39 return kDifOtpCtrlLockableBadArg;
40 }
41 if (checks_are_locked(otp)) {
42 return kDifOtpCtrlLockableLocked;
43 }
44
45 mmio_region_write32(otp->params.base_addr, OTP_CTRL_CHECK_TIMEOUT_REG_OFFSET,
46 config.check_timeout);
47 mmio_region_write32(otp->params.base_addr,
48 OTP_CTRL_INTEGRITY_CHECK_PERIOD_REG_OFFSET,
49 config.integrity_period_mask);
50 mmio_region_write32(otp->params.base_addr,
51 OTP_CTRL_CONSISTENCY_CHECK_PERIOD_REG_OFFSET,
52 config.consistency_period_mask);
53
54 return kDifOtpCtrlLockableOk;
55}
56
57dif_otp_ctrl_lockable_result_t dif_otp_ctrl_check_integrity(
58 const dif_otp_ctrl_t *otp) {
59 if (otp == NULL) {
60 return kDifOtpCtrlLockableBadArg;
61 }
62 if (checks_are_locked(otp)) {
63 return kDifOtpCtrlLockableLocked;
64 }
65
66 uint32_t reg =
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -050067 bitfield_bit32_write(0, OTP_CTRL_CHECK_TRIGGER_INTEGRITY_BIT, true);
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -040068 mmio_region_write32(otp->params.base_addr, OTP_CTRL_CHECK_TRIGGER_REG_OFFSET,
69 reg);
70
71 return kDifOtpCtrlLockableOk;
72}
73
74dif_otp_ctrl_lockable_result_t dif_otp_ctrl_check_consistency(
75 const dif_otp_ctrl_t *otp) {
76 if (otp == NULL) {
77 return kDifOtpCtrlLockableBadArg;
78 }
79 if (checks_are_locked(otp)) {
80 return kDifOtpCtrlLockableLocked;
81 }
82
83 uint32_t reg =
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -050084 bitfield_bit32_write(0, OTP_CTRL_CHECK_TRIGGER_CONSISTENCY_BIT, true);
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -040085 mmio_region_write32(otp->params.base_addr, OTP_CTRL_CHECK_TRIGGER_REG_OFFSET,
86 reg);
87
88 return kDifOtpCtrlLockableOk;
89}
90
91dif_otp_ctrl_result_t dif_otp_ctrl_lock_config(const dif_otp_ctrl_t *otp) {
92 if (otp == NULL) {
93 return kDifOtpCtrlBadArg;
94 }
95
96 uint32_t reg =
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -050097 bitfield_bit32_write(0, OTP_CTRL_CHECK_REGWEN_CHECK_REGWEN_BIT, true);
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -040098 mmio_region_write32(otp->params.base_addr, OTP_CTRL_CHECK_REGWEN_REG_OFFSET,
99 reg);
100
101 return kDifOtpCtrlOk;
102}
103
104dif_otp_ctrl_result_t dif_otp_ctrl_config_is_locked(const dif_otp_ctrl_t *otp,
105 bool *is_locked) {
106 if (otp == NULL || is_locked == NULL) {
107 return kDifOtpCtrlBadArg;
108 }
109
110 *is_locked = checks_are_locked(otp);
111 return kDifOtpCtrlOk;
112}
113
114static bool sw_read_lock_reg_offset(dif_otp_ctrl_partition_t partition,
115 ptrdiff_t *reg_offset,
116 bitfield_bit32_index_t *index) {
117 switch (partition) {
118 case kDifOtpCtrlPartitionCreatorSwCfg:
119 *reg_offset = OTP_CTRL_CREATOR_SW_CFG_READ_LOCK_REG_OFFSET;
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -0500120 *index = OTP_CTRL_CREATOR_SW_CFG_READ_LOCK_CREATOR_SW_CFG_READ_LOCK_BIT;
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -0400121 break;
122 case kDifOtpCtrlPartitionOwnerSwCfg:
123 *reg_offset = OTP_CTRL_OWNER_SW_CFG_READ_LOCK_REG_OFFSET;
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -0500124 *index = OTP_CTRL_OWNER_SW_CFG_READ_LOCK_OWNER_SW_CFG_READ_LOCK_BIT;
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -0400125 break;
126 default:
127 return false;
128 }
129 return true;
130}
131
132dif_otp_ctrl_result_t dif_otp_ctrl_lock_reading(
133 const dif_otp_ctrl_t *otp, dif_otp_ctrl_partition_t partition) {
134 if (otp == NULL) {
135 return kDifOtpCtrlBadArg;
136 }
137
138 ptrdiff_t offset;
139 bitfield_bit32_index_t index;
140 if (!sw_read_lock_reg_offset(partition, &offset, &index)) {
141 return kDifOtpCtrlBadArg;
142 }
143
144 uint32_t reg = bitfield_bit32_write(0, index, true);
145 mmio_region_write32(otp->params.base_addr, offset, reg);
146
147 return kDifOtpCtrlOk;
148}
149
150dif_otp_ctrl_result_t dif_otp_ctrl_reading_is_locked(
151 const dif_otp_ctrl_t *otp, dif_otp_ctrl_partition_t partition,
152 bool *is_locked) {
153 if (otp == NULL || is_locked == NULL) {
154 return kDifOtpCtrlBadArg;
155 }
156
157 ptrdiff_t offset;
158 bitfield_bit32_index_t index;
159 if (!sw_read_lock_reg_offset(partition, &offset, &index)) {
160 return kDifOtpCtrlBadArg;
161 }
162
163 uint32_t reg = mmio_region_read32(otp->params.base_addr, offset);
164 *is_locked = !bitfield_bit32_read(reg, index);
165 return kDifOtpCtrlOk;
166}
167
168static bool irq_index(dif_otp_ctrl_irq_t irq, bitfield_bit32_index_t *index) {
169 switch (irq) {
170 case kDifOtpCtrlIrqDone:
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -0500171 *index = OTP_CTRL_INTR_COMMON_OTP_OPERATION_DONE_BIT;
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -0400172 break;
173 case kDifOtpCtrlIrqError:
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -0500174 *index = OTP_CTRL_INTR_COMMON_OTP_ERROR_BIT;
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -0400175 break;
176 default:
177 return false;
178 }
179 return true;
180}
181
182dif_otp_ctrl_result_t dif_otp_ctrl_irq_is_pending(const dif_otp_ctrl_t *otp,
183 dif_otp_ctrl_irq_t irq,
184 bool *is_pending) {
185 if (otp == NULL || is_pending == NULL) {
186 return kDifOtpCtrlBadArg;
187 }
188
189 bitfield_bit32_index_t index;
190 if (!irq_index(irq, &index)) {
191 return kDifOtpCtrlBadArg;
192 }
193
194 uint32_t reg =
195 mmio_region_read32(otp->params.base_addr, OTP_CTRL_INTR_STATE_REG_OFFSET);
196 *is_pending = bitfield_bit32_read(reg, index);
197
198 return kDifOtpCtrlOk;
199}
200
201dif_otp_ctrl_result_t dif_otp_ctrl_irq_acknowledge(const dif_otp_ctrl_t *otp,
202 dif_otp_ctrl_irq_t irq) {
203 if (otp == NULL) {
204 return kDifOtpCtrlBadArg;
205 }
206
207 bitfield_bit32_index_t index;
208 if (!irq_index(irq, &index)) {
209 return kDifOtpCtrlBadArg;
210 }
211
212 uint32_t reg = bitfield_bit32_write(0, index, true);
213 mmio_region_write32(otp->params.base_addr, OTP_CTRL_INTR_STATE_REG_OFFSET,
214 reg);
215
216 return kDifOtpCtrlOk;
217}
218
219dif_otp_ctrl_result_t dif_otp_ctrl_irq_get_enabled(
220 const dif_otp_ctrl_t *otp, dif_otp_ctrl_irq_t irq,
221 dif_otp_ctrl_toggle_t *state) {
222 if (otp == NULL || state == NULL) {
223 return kDifOtpCtrlBadArg;
224 }
225
226 bitfield_bit32_index_t index;
227 if (!irq_index(irq, &index)) {
228 return kDifOtpCtrlBadArg;
229 }
230
231 uint32_t reg = mmio_region_read32(otp->params.base_addr,
232 OTP_CTRL_INTR_ENABLE_REG_OFFSET);
233 *state = bitfield_bit32_read(reg, index) ? kDifOtpCtrlToggleEnabled
234 : kDifOtpCtrlToggleDisabled;
235
236 return kDifOtpCtrlOk;
237}
238
239dif_otp_ctrl_result_t dif_otp_ctrl_irq_set_enabled(
240 const dif_otp_ctrl_t *otp, dif_otp_ctrl_irq_t irq,
241 dif_otp_ctrl_toggle_t state) {
242 if (otp == NULL) {
243 return kDifOtpCtrlBadArg;
244 }
245
246 bitfield_bit32_index_t index;
247 if (!irq_index(irq, &index)) {
248 return kDifOtpCtrlBadArg;
249 }
250
251 bool flag;
252 switch (state) {
253 case kDifOtpCtrlToggleEnabled:
254 flag = true;
255 break;
256 case kDifOtpCtrlToggleDisabled:
257 flag = false;
258 break;
259 default:
260 return kDifOtpCtrlBadArg;
261 }
262
263 uint32_t reg = mmio_region_read32(otp->params.base_addr,
264 OTP_CTRL_INTR_ENABLE_REG_OFFSET);
265 reg = bitfield_bit32_write(reg, index, flag);
266 mmio_region_write32(otp->params.base_addr, OTP_CTRL_INTR_ENABLE_REG_OFFSET,
267 reg);
268
269 return kDifOtpCtrlOk;
270}
271
272dif_otp_ctrl_result_t dif_otp_ctrl_irq_force(const dif_otp_ctrl_t *otp,
273 dif_otp_ctrl_irq_t irq) {
274 if (otp == NULL) {
275 return kDifOtpCtrlBadArg;
276 }
277
278 bitfield_bit32_index_t index;
279 if (!irq_index(irq, &index)) {
280 return kDifOtpCtrlBadArg;
281 }
282
283 uint32_t reg = bitfield_bit32_write(0, index, true);
284 mmio_region_write32(otp->params.base_addr, OTP_CTRL_INTR_TEST_REG_OFFSET,
285 reg);
286
287 return kDifOtpCtrlOk;
288}
289
290dif_otp_ctrl_result_t dif_otp_ctrl_irq_disable_all(
291 const dif_otp_ctrl_t *otp, dif_otp_ctrl_irq_snapshot_t *snapshot) {
292 if (otp == NULL) {
293 return kDifOtpCtrlBadArg;
294 }
295
296 if (snapshot != NULL) {
297 *snapshot = mmio_region_read32(otp->params.base_addr,
298 OTP_CTRL_INTR_ENABLE_REG_OFFSET);
299 }
300
301 mmio_region_write32(otp->params.base_addr, OTP_CTRL_INTR_ENABLE_REG_OFFSET,
302 0);
303 return kDifOtpCtrlOk;
304}
305
306dif_otp_ctrl_result_t dif_otp_ctrl_irq_restore_all(
307 const dif_otp_ctrl_t *otp, const dif_otp_ctrl_irq_snapshot_t *snapshot) {
308 if (otp == NULL || snapshot == NULL) {
309 return kDifOtpCtrlBadArg;
310 }
311
312 mmio_region_write32(otp->params.base_addr, OTP_CTRL_INTR_ENABLE_REG_OFFSET,
313 *snapshot);
314 return kDifOtpCtrlOk;
315}
316
317dif_otp_ctrl_result_t dif_otp_ctrl_get_status(const dif_otp_ctrl_t *otp,
318 dif_otp_ctrl_status_t *status) {
319 if (otp == NULL || status == NULL) {
320 return kDifOtpCtrlBadArg;
321 }
322
323 static const bitfield_bit32_index_t kIndices[] = {
324 [kDifOtpCtrlStatusCodeCreatorSwCfgError] =
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -0500325 OTP_CTRL_STATUS_CREATOR_SW_CFG_ERROR_BIT,
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -0400326 [kDifOtpCtrlStatusCodeOwnerSwCfgError] =
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -0500327 OTP_CTRL_STATUS_OWNER_SW_CFG_ERROR_BIT,
328 [kDifOtpCtrlStatusCodeHwCfgError] = OTP_CTRL_STATUS_HW_CFG_ERROR_BIT,
329 [kDifOtpCtrlStatusCodeLifeCycleError] =
330 OTP_CTRL_STATUS_LIFE_CYCLE_ERROR_BIT,
331 [kDifOtpCtrlStatusCodeSecret0Error] = OTP_CTRL_STATUS_SECRET0_ERROR_BIT,
332 [kDifOtpCtrlStatusCodeSecret1Error] = OTP_CTRL_STATUS_SECRET1_ERROR_BIT,
333 [kDifOtpCtrlStatusCodeSecret2Error] = OTP_CTRL_STATUS_SECRET2_ERROR_BIT,
334 [kDifOtpCtrlStatusCodeDaiError] = OTP_CTRL_STATUS_DAI_ERROR_BIT,
335 [kDifOtpCtrlStatusCodeLciError] = OTP_CTRL_STATUS_LCI_ERROR_BIT,
336 [kDifOtpCtrlStatusCodeTimeoutError] = OTP_CTRL_STATUS_TIMEOUT_ERROR_BIT,
337 [kDifOtpCtrlStatusCodeLfsrError] = OTP_CTRL_STATUS_LFSR_FSM_ERROR_BIT,
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -0400338 [kDifOtpCtrlStatusCodeScramblingError] =
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -0500339 OTP_CTRL_STATUS_SCRAMBLING_FSM_ERROR_BIT,
340 [kDifOtpCtrlStatusCodeKdfError] = OTP_CTRL_STATUS_KEY_DERIV_FSM_ERROR_BIT,
Michael Schaffnera28c2802021-06-29 11:50:26 -0700341 [kDifOtpCtrlStatusCodeBusIntegError] =
342 OTP_CTRL_STATUS_BUS_INTEG_ERROR_BIT,
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -0500343 [kDifOtpCtrlStatusCodeDaiIdle] = OTP_CTRL_STATUS_DAI_IDLE_BIT,
344 [kDifOtpCtrlStatusCodeCheckPending] = OTP_CTRL_STATUS_CHECK_PENDING_BIT,
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -0400345 };
346
347 status->codes = 0;
348 uint32_t status_code =
349 mmio_region_read32(otp->params.base_addr, OTP_CTRL_STATUS_REG_OFFSET);
350 uint32_t error_codes =
351 mmio_region_read32(otp->params.base_addr, OTP_CTRL_ERR_CODE_REG_OFFSET);
352 for (int i = 0; i < ARRAYSIZE(kIndices); ++i) {
353 // If the error is not present at all, we clear its cause bit if relevant,
354 // and bail immediately.
355 if (!bitfield_bit32_read(status_code, kIndices[i])) {
356 if (i <= kDifOtpCtrlStatusCodeHasCauseLast) {
357 status->causes[i] = kDifOtpCtrlErrorOk;
358 }
359 continue;
360 }
361
362 status->codes = bitfield_bit32_write(status->codes, i, true);
363
364 bitfield_field32_t field;
365 switch (i) {
366 case kDifOtpCtrlStatusCodeCreatorSwCfgError:
367 field = (bitfield_field32_t){
368 .mask = OTP_CTRL_ERR_CODE_ERR_CODE_0_MASK,
369 .index = OTP_CTRL_ERR_CODE_ERR_CODE_0_OFFSET,
370 };
371 break;
372 case kDifOtpCtrlStatusCodeOwnerSwCfgError:
373 field = (bitfield_field32_t){
374 .mask = OTP_CTRL_ERR_CODE_ERR_CODE_1_MASK,
375 .index = OTP_CTRL_ERR_CODE_ERR_CODE_1_OFFSET,
376 };
377 break;
378 case kDifOtpCtrlStatusCodeHwCfgError:
379 field = (bitfield_field32_t){
380 .mask = OTP_CTRL_ERR_CODE_ERR_CODE_2_MASK,
381 .index = OTP_CTRL_ERR_CODE_ERR_CODE_2_OFFSET,
382 };
383 break;
384 case kDifOtpCtrlStatusCodeSecret0Error:
385 field = (bitfield_field32_t){
386 .mask = OTP_CTRL_ERR_CODE_ERR_CODE_3_MASK,
387 .index = OTP_CTRL_ERR_CODE_ERR_CODE_3_OFFSET,
388 };
389 break;
390 case kDifOtpCtrlStatusCodeSecret1Error:
391 field = (bitfield_field32_t){
392 .mask = OTP_CTRL_ERR_CODE_ERR_CODE_4_MASK,
393 .index = OTP_CTRL_ERR_CODE_ERR_CODE_4_OFFSET,
394 };
395 break;
396 case kDifOtpCtrlStatusCodeSecret2Error:
397 field = (bitfield_field32_t){
398 .mask = OTP_CTRL_ERR_CODE_ERR_CODE_5_MASK,
399 .index = OTP_CTRL_ERR_CODE_ERR_CODE_5_OFFSET,
400 };
401 break;
402 case kDifOtpCtrlStatusCodeLifeCycleError:
403 field = (bitfield_field32_t){
404 .mask = OTP_CTRL_ERR_CODE_ERR_CODE_6_MASK,
405 .index = OTP_CTRL_ERR_CODE_ERR_CODE_6_OFFSET,
406 };
407 break;
408 case kDifOtpCtrlStatusCodeDaiError:
409 field = (bitfield_field32_t){
410 .mask = OTP_CTRL_ERR_CODE_ERR_CODE_7_MASK,
411 .index = OTP_CTRL_ERR_CODE_ERR_CODE_7_OFFSET,
412 };
413 break;
414 case kDifOtpCtrlStatusCodeLciError:
415 field = (bitfield_field32_t){
416 .mask = OTP_CTRL_ERR_CODE_ERR_CODE_8_MASK,
417 .index = OTP_CTRL_ERR_CODE_ERR_CODE_8_OFFSET,
418 };
419 break;
420 // Not an error status, so there's nothing to do.
421 default:
422 continue;
423 }
424
425 dif_otp_ctrl_error_t err;
426 switch (bitfield_field32_read(error_codes, field)) {
427 case OTP_CTRL_ERR_CODE_ERR_CODE_0_VALUE_NO_ERROR:
428 err = kDifOtpCtrlErrorOk;
429 break;
430 case OTP_CTRL_ERR_CODE_ERR_CODE_0_VALUE_MACRO_ERROR:
431 err = kDifOtpCtrlErrorMacroUnspecified;
432 break;
433 case OTP_CTRL_ERR_CODE_ERR_CODE_0_VALUE_MACRO_ECC_CORR_ERROR:
434 err = kDifOtpCtrlErrorMacroRecoverableRead;
435 break;
436 case OTP_CTRL_ERR_CODE_ERR_CODE_0_VALUE_MACRO_ECC_UNCORR_ERROR:
437 err = kDifOtpCtrlErrorMacroUnrecoverableRead;
438 break;
439 case OTP_CTRL_ERR_CODE_ERR_CODE_0_VALUE_MACRO_WRITE_BLANK_ERROR:
440 err = kDifOtpCtrlErrorMacroBlankCheckFailed;
441 break;
442 case OTP_CTRL_ERR_CODE_ERR_CODE_0_VALUE_ACCESS_ERROR:
443 err = kDifOtpCtrlErrorLockedAccess;
444 break;
445 case OTP_CTRL_ERR_CODE_ERR_CODE_0_VALUE_CHECK_FAIL_ERROR:
446 err = kDifOtpCtrlErrorBackgroundCheckFailed;
447 break;
448 case OTP_CTRL_ERR_CODE_ERR_CODE_0_VALUE_FSM_STATE_ERROR:
449 err = kDifOtpCtrlErrorFsmBadState;
450 break;
451 default:
452 return kDifOtpCtrlError;
453 }
454 status->causes[i] = err;
455 }
456
457 return kDifOtpCtrlOk;
458}
459
460typedef struct partition_info {
461 /**
462 * The absolute OTP address at which this partition starts.
463 */
464 uint32_t start_addr;
465 /**
466 * The length of this partition, in bytes, including the digest.
467 *
468 * If the partition has a digest, it is expected to be at address
469 * `start_addr + len - sizeof(uint64_t)`.
470 */
471 uint32_t len;
472 /**
473 * The alignment mask for this partition.
474 *
475 * A valid address for this partition must be such that
476 * `addr & align_mask == 0`.
477 */
478 uint32_t align_mask;
479
480 /**
481 * Whether this is a software-managed partition with a software-managed
482 * digest.
483 */
484 bool is_software;
485} partition_info_t;
486
487static const partition_info_t kPartitions[] = {
488 // TODO: These should be provided by the gen'd header.
Miguel Young de la Sota465676b2020-11-04 09:52:47 -0500489 // See #3904.
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -0400490 [kDifOtpCtrlPartitionCreatorSwCfg] =
491 {
492 .start_addr = 0,
493 .len = 0x300,
494 .align_mask = 0x3,
495 .is_software = true,
496 },
497 [kDifOtpCtrlPartitionOwnerSwCfg] =
498 {
499 .start_addr = 0x300,
500 .len = 0x300,
501 .align_mask = 0x3,
502 .is_software = true,
503 },
504 [kDifOtpCtrlPartitionHwCfg] =
505 {
506 .start_addr = 0x600,
507 .len = 0xb0,
508 .align_mask = 0x3,
509 },
510 [kDifOtpCtrlPartitionSecret0] =
511 {
512 .start_addr = 0x6b0,
513 .len = 0x28,
514 .align_mask = 0x7,
515 },
516 [kDifOtpCtrlPartitionSecret1] =
517 {
518 .start_addr = 0x6d8,
519 .len = 0x58,
520 .align_mask = 0x7,
521 },
522 [kDifOtpCtrlPartitionSecret2] =
523 {
524 .start_addr = 0x730,
525 .len = 0x58,
526 .align_mask = 0x7,
527 },
528 [kDifOtpCtrlPartitionLifeCycle] =
529 {
530 .start_addr = 0x7a8,
531 .len = 0x58,
532 .align_mask = 0x3,
533 },
534};
535
536dif_otp_ctrl_dai_result_t dif_otp_ctrl_dai_read_start(
537 const dif_otp_ctrl_t *otp, dif_otp_ctrl_partition_t partition,
538 uint32_t address) {
539 if (otp == NULL || partition >= ARRAYSIZE(kPartitions)) {
540 return kDifOtpCtrlDaiBadArg;
541 }
542
543 if ((address & kPartitions[partition].align_mask) != 0) {
544 return kDifOtpCtrlDaiUnaligned;
545 }
546
547 if (address >= kPartitions[partition].len) {
548 return kDifOtpCtrlDaiOutOfRange;
549 }
550
551 uint32_t busy = mmio_region_read32(otp->params.base_addr,
552 OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET);
553 if (!bitfield_bit32_read(
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -0500554 busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT)) {
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -0400555 return kDifOtpCtrlDaiBusy;
556 }
557
558 address += kPartitions[partition].start_addr;
559 mmio_region_write32(otp->params.base_addr,
560 OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, address);
561
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -0500562 uint32_t cmd =
Srikrishna Iyerd9d84672020-11-04 01:55:34 -0800563 bitfield_bit32_write(0, OTP_CTRL_DIRECT_ACCESS_CMD_RD_BIT, true);
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -0400564 mmio_region_write32(otp->params.base_addr,
565 OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET, cmd);
566
567 return kDifOtpCtrlDaiOk;
568}
569
570dif_otp_ctrl_dai_result_t dif_otp_ctrl_dai_read32_end(const dif_otp_ctrl_t *otp,
571 uint32_t *value) {
572 if (otp == NULL || value == NULL) {
573 return kDifOtpCtrlDaiBadArg;
574 }
575
576 uint32_t busy = mmio_region_read32(otp->params.base_addr,
577 OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET);
578 if (!bitfield_bit32_read(
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -0500579 busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT)) {
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -0400580 return kDifOtpCtrlDaiBusy;
581 }
582
583 *value = mmio_region_read32(otp->params.base_addr,
584 OTP_CTRL_DIRECT_ACCESS_RDATA_0_REG_OFFSET);
585 return kDifOtpCtrlDaiOk;
586}
587
588dif_otp_ctrl_dai_result_t dif_otp_ctrl_dai_read64_end(const dif_otp_ctrl_t *otp,
589 uint64_t *value) {
590 if (otp == NULL || value == NULL) {
591 return kDifOtpCtrlDaiBadArg;
592 }
593
594 uint32_t busy = mmio_region_read32(otp->params.base_addr,
595 OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET);
596 if (!bitfield_bit32_read(
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -0500597 busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT)) {
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -0400598 return kDifOtpCtrlDaiBusy;
599 }
600
601 *value = mmio_region_read32(otp->params.base_addr,
602 OTP_CTRL_DIRECT_ACCESS_RDATA_1_REG_OFFSET);
603 *value <<= 32;
604 *value |= mmio_region_read32(otp->params.base_addr,
605 OTP_CTRL_DIRECT_ACCESS_RDATA_0_REG_OFFSET);
606 return kDifOtpCtrlDaiOk;
607}
608
609dif_otp_ctrl_dai_result_t dif_otp_ctrl_dai_program32(
610 const dif_otp_ctrl_t *otp, dif_otp_ctrl_partition_t partition,
611 uint32_t address, uint32_t value) {
612 if (otp == NULL || partition >= ARRAYSIZE(kPartitions)) {
613 return kDifOtpCtrlDaiBadArg;
614 }
615
616 // Ensure that we are writing to a 32-bit-access partition by checking that
617 // the alignment mask is 0b11.
618 //
619 // Note furthermore that the LC partition is *not* writeable, so we eject
620 // here.
621 if (kPartitions[partition].align_mask != 0x3 ||
622 partition == kDifOtpCtrlPartitionLifeCycle) {
623 return kDifOtpCtrlDaiBadPartition;
624 }
625
626 if ((address & kPartitions[partition].align_mask) != 0) {
627 return kDifOtpCtrlDaiUnaligned;
628 }
629
630 // NOTE: The bounds check is tightened here, since we disallow writing the
631 // digest directly.
632 size_t digest_size = sizeof(uint64_t);
633 if (address >= kPartitions[partition].len - digest_size) {
634 return kDifOtpCtrlDaiOutOfRange;
635 }
636
637 uint32_t busy = mmio_region_read32(otp->params.base_addr,
638 OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET);
639 if (!bitfield_bit32_read(
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -0500640 busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT)) {
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -0400641 return kDifOtpCtrlDaiBusy;
642 }
643
644 address += kPartitions[partition].start_addr;
645 mmio_region_write32(otp->params.base_addr,
646 OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, address);
647
648 mmio_region_write32(otp->params.base_addr,
649 OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET, value);
650
651 uint32_t cmd =
Srikrishna Iyerd9d84672020-11-04 01:55:34 -0800652 bitfield_bit32_write(0, OTP_CTRL_DIRECT_ACCESS_CMD_WR_BIT, true);
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -0400653 mmio_region_write32(otp->params.base_addr,
654 OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET, cmd);
655
656 return kDifOtpCtrlDaiOk;
657}
658
659dif_otp_ctrl_dai_result_t dif_otp_ctrl_dai_program64(
660 const dif_otp_ctrl_t *otp, dif_otp_ctrl_partition_t partition,
661 uint32_t address, uint64_t value) {
662 if (otp == NULL || partition >= ARRAYSIZE(kPartitions)) {
663 return kDifOtpCtrlDaiBadArg;
664 }
665
666 // Ensure that we are writing to a 32-bit-access partition by checking that
667 // the alignment mask is 0b11.
668 if (kPartitions[partition].align_mask != 0x7) {
669 return kDifOtpCtrlDaiBadPartition;
670 }
671
672 if ((address & kPartitions[partition].align_mask) != 0) {
673 return kDifOtpCtrlDaiUnaligned;
674 }
675
676 // NOTE: The bounds check is tightened here, since we disallow writing the
677 // digest directly.
678 size_t digest_size = sizeof(uint64_t);
679 if (address >= kPartitions[partition].len - digest_size) {
680 return kDifOtpCtrlDaiOutOfRange;
681 }
682
683 uint32_t busy = mmio_region_read32(otp->params.base_addr,
684 OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET);
685 if (!bitfield_bit32_read(
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -0500686 busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT)) {
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -0400687 return kDifOtpCtrlDaiBusy;
688 }
689
690 address += kPartitions[partition].start_addr;
691 mmio_region_write32(otp->params.base_addr,
692 OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, address);
693
694 mmio_region_write32(otp->params.base_addr,
695 OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET,
696 value & UINT32_MAX);
697 mmio_region_write32(otp->params.base_addr,
698 OTP_CTRL_DIRECT_ACCESS_WDATA_1_REG_OFFSET, value >> 32);
699
700 uint32_t cmd =
Srikrishna Iyerd9d84672020-11-04 01:55:34 -0800701 bitfield_bit32_write(0, OTP_CTRL_DIRECT_ACCESS_CMD_WR_BIT, true);
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -0400702 mmio_region_write32(otp->params.base_addr,
703 OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET, cmd);
704
705 return kDifOtpCtrlDaiOk;
706}
707
708dif_otp_ctrl_dai_result_t dif_otp_ctrl_dai_digest(
709 const dif_otp_ctrl_t *otp, dif_otp_ctrl_partition_t partition,
710 uint64_t digest) {
711 if (otp == NULL || partition >= ARRAYSIZE(kPartitions)) {
712 return kDifOtpCtrlDaiBadArg;
713 }
714
715 // The LC partition does not have a digest.
716 if (partition == kDifOtpCtrlPartitionLifeCycle) {
717 return kDifOtpCtrlDaiBadPartition;
718 }
719
720 // For software partitions, the digest must be nonzero; for all other
721 // partitions it must be zero.
722 bool is_sw = kPartitions[partition].is_software;
723 if (is_sw == (digest == 0)) {
724 return kDifOtpCtrlDaiBadArg;
725 }
726
727 uint32_t busy = mmio_region_read32(otp->params.base_addr,
728 OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET);
729 if (!bitfield_bit32_read(
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -0500730 busy, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT)) {
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -0400731 return kDifOtpCtrlDaiBusy;
732 }
733
734 uint32_t address = kPartitions[partition].start_addr;
735 if (is_sw) {
736 address += kPartitions[partition].len - sizeof(digest);
737 }
738 mmio_region_write32(otp->params.base_addr,
739 OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, address);
740
741 if (digest != 0) {
742 mmio_region_write32(otp->params.base_addr,
743 OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET,
744 digest & 0xffffffff);
745 mmio_region_write32(otp->params.base_addr,
746 OTP_CTRL_DIRECT_ACCESS_WDATA_1_REG_OFFSET,
747 digest >> 32);
748 }
749
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -0500750 bitfield_bit32_index_t cmd_bit = is_sw
Srikrishna Iyerd9d84672020-11-04 01:55:34 -0800751 ? OTP_CTRL_DIRECT_ACCESS_CMD_WR_BIT
Miguel Young de la Sota4e1a4a32020-11-05 10:08:52 -0500752 : OTP_CTRL_DIRECT_ACCESS_CMD_DIGEST_BIT;
Miguel Young de la Sota2eaa4062020-10-22 13:23:32 -0400753 uint32_t cmd = bitfield_bit32_write(0, cmd_bit, true);
754 mmio_region_write32(otp->params.base_addr,
755 OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET, cmd);
756
757 return kDifOtpCtrlDaiOk;
758}
759
760dif_otp_ctrl_digest_result_t dif_otp_ctrl_get_digest(
761 const dif_otp_ctrl_t *otp, dif_otp_ctrl_partition_t partition,
762 uint64_t *digest) {
763 if (otp == NULL || digest == NULL) {
764 return kDifOtpCtrlDigestBadArg;
765 }
766
767 // The LC partition does not have a digest.
768 if (partition == kDifOtpCtrlPartitionLifeCycle) {
769 return kDifOtpCtrlDigestBadArg;
770 }
771
772 ptrdiff_t reg0, reg1;
773 switch (partition) {
774 case kDifOtpCtrlPartitionCreatorSwCfg:
775 reg0 = OTP_CTRL_CREATOR_SW_CFG_DIGEST_0_REG_OFFSET;
776 reg1 = OTP_CTRL_CREATOR_SW_CFG_DIGEST_1_REG_OFFSET;
777 break;
778 case kDifOtpCtrlPartitionOwnerSwCfg:
779 reg0 = OTP_CTRL_OWNER_SW_CFG_DIGEST_0_REG_OFFSET;
780 reg1 = OTP_CTRL_OWNER_SW_CFG_DIGEST_1_REG_OFFSET;
781 break;
782 case kDifOtpCtrlPartitionHwCfg:
783 reg0 = OTP_CTRL_HW_CFG_DIGEST_0_REG_OFFSET;
784 reg1 = OTP_CTRL_HW_CFG_DIGEST_1_REG_OFFSET;
785 break;
786 case kDifOtpCtrlPartitionSecret0:
787 reg0 = OTP_CTRL_SECRET0_DIGEST_0_REG_OFFSET;
788 reg1 = OTP_CTRL_SECRET0_DIGEST_1_REG_OFFSET;
789 break;
790 case kDifOtpCtrlPartitionSecret1:
791 reg0 = OTP_CTRL_SECRET1_DIGEST_0_REG_OFFSET;
792 reg1 = OTP_CTRL_SECRET1_DIGEST_1_REG_OFFSET;
793 break;
794 case kDifOtpCtrlPartitionSecret2:
795 reg0 = OTP_CTRL_SECRET2_DIGEST_0_REG_OFFSET;
796 reg1 = OTP_CTRL_SECRET2_DIGEST_1_REG_OFFSET;
797 break;
798 default:
799 return kDifOtpCtrlDigestBadArg;
800 }
801
802 uint64_t value = mmio_region_read32(otp->params.base_addr, reg1);
803 value <<= 32;
804 value |= mmio_region_read32(otp->params.base_addr, reg0);
805
806 if (value == 0) {
807 return kDifOtpCtrlDigestMissing;
808 }
809 *digest = value;
810
811 return kDifOtpCtrlDigestOk;
812}
813
814dif_otp_ctrl_dai_result_t dif_otp_ctrl_read_blocking(
815 const dif_otp_ctrl_t *otp, dif_otp_ctrl_partition_t partition,
816 uint32_t address, uint32_t *buf, size_t len) {
817 if (otp == NULL || partition >= ARRAYSIZE(kPartitions) || buf == NULL) {
818 return kDifOtpCtrlDaiBadArg;
819 }
820
821 if (!kPartitions[partition].is_software) {
822 return kDifOtpCtrlDaiBadPartition;
823 }
824
825 if ((address & kPartitions[partition].align_mask) != 0) {
826 return kDifOtpCtrlDaiUnaligned;
827 }
828
829 if (address + len >= kPartitions[partition].len) {
830 return kDifOtpCtrlDaiOutOfRange;
831 }
832
833 ptrdiff_t reg_offset = OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET +
834 kPartitions[partition].start_addr + address;
835 mmio_region_memcpy_from_mmio32(otp->params.base_addr, reg_offset, buf,
836 len * sizeof(uint32_t));
837 return kDifOtpCtrlDaiOk;
838}
839
840dif_otp_ctrl_result_t dif_otp_ctrl_read_test(const dif_otp_ctrl_t *otp,
841 uint32_t address, uint32_t *buf,
842 size_t len) {
843 if (otp == NULL || buf == NULL) {
844 return kDifOtpCtrlBadArg;
845 }
846
847 ptrdiff_t reg_offset = OTP_CTRL_TEST_ACCESS_REG_OFFSET + address;
848 mmio_region_memcpy_from_mmio32(otp->params.base_addr, reg_offset, buf,
849 len * sizeof(uint32_t));
850 return kDifOtpCtrlOk;
851}
852
853dif_otp_ctrl_result_t dif_otp_ctrl_write_test(const dif_otp_ctrl_t *otp,
854 uint32_t address,
855 const uint32_t *buf, size_t len) {
856 if (otp == NULL || buf == NULL) {
857 return kDifOtpCtrlBadArg;
858 }
859
860 ptrdiff_t reg_offset = OTP_CTRL_TEST_ACCESS_REG_OFFSET + address;
861 mmio_region_memcpy_to_mmio32(otp->params.base_addr, reg_offset, buf,
862 len * sizeof(uint32_t));
863 return kDifOtpCtrlOk;
864}