Software APIs
dif_lc_ctrl.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
6
9#include "sw/device/lib/base/multibits.h"
10
11#include "lc_ctrl_regs.h" // Generated.
12
13dif_result_t dif_lc_ctrl_get_state(const dif_lc_ctrl_t *lc,
14 dif_lc_ctrl_state_t *state) {
15 if (lc == NULL || state == NULL) {
16 return kDifBadArg;
17 }
18
19 uint32_t reg = mmio_region_read32(lc->base_addr, LC_CTRL_LC_STATE_REG_OFFSET);
20 switch (bitfield_field32_read(reg, LC_CTRL_LC_STATE_STATE_FIELD)) {
21 case LC_CTRL_LC_STATE_STATE_VALUE_RAW:
22 *state = kDifLcCtrlStateRaw;
23 break;
24 case LC_CTRL_LC_STATE_STATE_VALUE_TEST_UNLOCKED0:
26 break;
27 case LC_CTRL_LC_STATE_STATE_VALUE_TEST_LOCKED0:
29 break;
30 case LC_CTRL_LC_STATE_STATE_VALUE_TEST_UNLOCKED1:
32 break;
33 case LC_CTRL_LC_STATE_STATE_VALUE_TEST_LOCKED1:
35 break;
36 case LC_CTRL_LC_STATE_STATE_VALUE_TEST_UNLOCKED2:
38 break;
39 case LC_CTRL_LC_STATE_STATE_VALUE_TEST_LOCKED2:
41 break;
42 case LC_CTRL_LC_STATE_STATE_VALUE_TEST_UNLOCKED3:
44 break;
45 case LC_CTRL_LC_STATE_STATE_VALUE_TEST_LOCKED3:
47 break;
48 case LC_CTRL_LC_STATE_STATE_VALUE_TEST_UNLOCKED4:
50 break;
51 case LC_CTRL_LC_STATE_STATE_VALUE_TEST_LOCKED4:
53 break;
54 case LC_CTRL_LC_STATE_STATE_VALUE_TEST_UNLOCKED5:
56 break;
57 case LC_CTRL_LC_STATE_STATE_VALUE_TEST_LOCKED5:
59 break;
60 case LC_CTRL_LC_STATE_STATE_VALUE_TEST_UNLOCKED6:
62 break;
63 case LC_CTRL_LC_STATE_STATE_VALUE_TEST_LOCKED6:
65 break;
66 case LC_CTRL_LC_STATE_STATE_VALUE_TEST_UNLOCKED7:
68 break;
69 case LC_CTRL_LC_STATE_STATE_VALUE_DEV:
70 *state = kDifLcCtrlStateDev;
71 break;
72 case LC_CTRL_LC_STATE_STATE_VALUE_PROD:
73 *state = kDifLcCtrlStateProd;
74 break;
75 case LC_CTRL_LC_STATE_STATE_VALUE_PROD_END:
77 break;
78 case LC_CTRL_LC_STATE_STATE_VALUE_RMA:
79 *state = kDifLcCtrlStateRma;
80 break;
81 case LC_CTRL_LC_STATE_STATE_VALUE_SCRAP:
82 *state = kDifLcCtrlStateScrap;
83 break;
84
85 case LC_CTRL_LC_STATE_STATE_VALUE_POST_TRANSITION:
87 break;
88 case LC_CTRL_LC_STATE_STATE_VALUE_ESCALATE:
90 break;
91 case LC_CTRL_LC_STATE_STATE_VALUE_INVALID:
93 break;
94
95 default:
96 return kDifError;
97 }
98
99 return kDifOk;
100}
101
102dif_result_t dif_lc_ctrl_get_attempts(const dif_lc_ctrl_t *lc, uint8_t *count) {
103 if (lc == NULL || count == NULL) {
104 return kDifBadArg;
105 }
106
107 uint32_t reg =
108 mmio_region_read32(lc->base_addr, LC_CTRL_LC_TRANSITION_CNT_REG_OFFSET);
109 uint8_t value =
110 (uint8_t)bitfield_field32_read(reg, LC_CTRL_LC_TRANSITION_CNT_CNT_FIELD);
111 if (value == LC_CTRL_LC_TRANSITION_CNT_CNT_MASK) {
112 return kDifError;
113 }
114
115 *count = value;
116 return kDifOk;
117}
118
119dif_result_t dif_lc_ctrl_get_status(const dif_lc_ctrl_t *lc,
121 if (lc == NULL || status == NULL) {
122 return kDifBadArg;
123 }
124
125 uint32_t reg = mmio_region_read32(lc->base_addr, LC_CTRL_STATUS_REG_OFFSET);
126
127 dif_lc_ctrl_status_t status_word = 0;
128
129 if (bitfield_bit32_read(reg, LC_CTRL_STATUS_INITIALIZED_BIT)) {
130 status_word = bitfield_bit32_write(status_word,
132 }
133
134 if (bitfield_bit32_read(reg, LC_CTRL_STATUS_READY_BIT)) {
135 status_word =
136 bitfield_bit32_write(status_word, kDifLcCtrlStatusCodeReady, true);
137 }
138
139 if (bitfield_bit32_read(reg, LC_CTRL_STATUS_EXT_CLOCK_SWITCHED_BIT)) {
140 status_word = bitfield_bit32_write(status_word,
142 }
143
144 if (bitfield_bit32_read(reg, LC_CTRL_STATUS_TRANSITION_SUCCESSFUL_BIT)) {
145 status_word =
146 bitfield_bit32_write(status_word, kDifLcCtrlStatusCodeSuccess, true);
147 }
148
149 if (bitfield_bit32_read(reg, LC_CTRL_STATUS_TRANSITION_COUNT_ERROR_BIT)) {
150 status_word = bitfield_bit32_write(
151 status_word, kDifLcCtrlStatusCodeTooManyTransitions, true);
152 }
153
154 if (bitfield_bit32_read(reg, LC_CTRL_STATUS_TRANSITION_ERROR_BIT)) {
155 status_word = bitfield_bit32_write(
156 status_word, kDifLcCtrlStatusCodeInvalidTransition, true);
157 }
158
159 if (bitfield_bit32_read(reg, LC_CTRL_STATUS_TOKEN_ERROR_BIT)) {
160 status_word =
161 bitfield_bit32_write(status_word, kDifLcCtrlStatusCodeBadToken, true);
162 }
163
164 if (bitfield_bit32_read(reg, LC_CTRL_STATUS_FLASH_RMA_ERROR_BIT)) {
165 status_word = bitfield_bit32_write(status_word,
167 }
168
169 if (bitfield_bit32_read(reg, LC_CTRL_STATUS_OTP_ERROR_BIT)) {
170 status_word =
171 bitfield_bit32_write(status_word, kDifLcCtrlStatusCodeOtpError, true);
172 }
173
174 if (bitfield_bit32_read(reg, LC_CTRL_STATUS_STATE_ERROR_BIT)) {
175 status_word =
176 bitfield_bit32_write(status_word, kDifLcCtrlStatusCodeCorrupt, true);
177 }
178
179 if (bitfield_bit32_read(reg, LC_CTRL_STATUS_BUS_INTEG_ERROR_BIT)) {
180 status_word = bitfield_bit32_write(status_word,
182 }
183
184 if (bitfield_bit32_read(reg, LC_CTRL_STATUS_OTP_PARTITION_ERROR_BIT)) {
185 status_word = bitfield_bit32_write(status_word,
187 }
188
189 *status = status_word;
190 return kDifOk;
191}
192
193dif_result_t dif_lc_ctrl_get_id_state(const dif_lc_ctrl_t *lc,
194 dif_lc_ctrl_id_state_t *state) {
195 if (lc == NULL || state == NULL) {
196 return kDifBadArg;
197 }
198
199 uint32_t reg =
200 mmio_region_read32(lc->base_addr, LC_CTRL_LC_ID_STATE_REG_OFFSET);
201 switch (reg) {
202 case LC_CTRL_LC_ID_STATE_STATE_VALUE_BLANK:
203 *state = kDifLcCtrlIdStateBlank;
204 break;
205 case LC_CTRL_LC_ID_STATE_STATE_VALUE_PERSONALIZED:
207 break;
208 case LC_CTRL_LC_ID_STATE_STATE_VALUE_INVALID:
210 break;
211 default:
212 return kDifError;
213 }
214
215 return kDifOk;
216}
217
218dif_result_t dif_lc_ctrl_get_hw_rev(const dif_lc_ctrl_t *lc,
219 dif_lc_ctrl_hw_rev_t *hw_rev) {
220 if (lc == NULL || hw_rev == NULL) {
221 return kDifBadArg;
222 }
223
224 uint32_t reg =
225 mmio_region_read32(lc->base_addr, LC_CTRL_HW_REVISION0_REG_OFFSET);
226 hw_rev->silicon_creator_id = (uint16_t)bitfield_field32_read(
227 reg, LC_CTRL_HW_REVISION0_SILICON_CREATOR_ID_FIELD);
228 hw_rev->product_id = (uint16_t)bitfield_field32_read(
229 reg, LC_CTRL_HW_REVISION0_PRODUCT_ID_FIELD);
230 hw_rev->revision_id = (uint8_t)bitfield_field32_read(
231 reg, LC_CTRL_HW_REVISION1_REVISION_ID_FIELD);
232 return kDifOk;
233}
234
235dif_result_t dif_lc_ctrl_get_device_id(const dif_lc_ctrl_t *lc,
236 dif_lc_ctrl_device_id_t *device_id) {
237 if (lc == NULL || device_id == NULL) {
238 return kDifBadArg;
239 }
240
241 mmio_region_memcpy_from_mmio32(lc->base_addr, LC_CTRL_DEVICE_ID_0_REG_OFFSET,
242 device_id->data,
243 ARRAYSIZE(device_id->data) * sizeof(uint32_t));
244 return kDifOk;
245}
246
247dif_result_t dif_lc_ctrl_mutex_try_acquire(const dif_lc_ctrl_t *lc) {
248 if (lc == NULL) {
249 return kDifBadArg;
250 }
251
252 // Check that mutex claim via SW is not locked.
253 if (mmio_region_read32(lc->base_addr,
254 LC_CTRL_CLAIM_TRANSITION_IF_REGWEN_REG_OFFSET) == 0) {
255 return kDifLocked;
256 }
257
258 mmio_region_write32(lc->base_addr, LC_CTRL_CLAIM_TRANSITION_IF_REG_OFFSET,
259 kMultiBitBool8True);
260 uint32_t reg =
261 mmio_region_read32(lc->base_addr, LC_CTRL_CLAIM_TRANSITION_IF_REG_OFFSET);
262 // If the register is not `kMultiBitBool8True`, that means we failed to take
263 // the mutex for whatever reason.
264 if (reg != kMultiBitBool8True) {
265 return kDifUnavailable;
266 } else {
267 return kDifOk;
268 }
269}
270
271dif_result_t dif_lc_ctrl_mutex_release(const dif_lc_ctrl_t *lc) {
272 if (lc == NULL) {
273 return kDifBadArg;
274 }
275
276 uint32_t reg =
277 mmio_region_read32(lc->base_addr, LC_CTRL_CLAIM_TRANSITION_IF_REG_OFFSET);
278 if (reg != kMultiBitBool8True) {
279 // We're not holding the mutex, which is a programmer error.
280 return kDifError;
281 }
282
283 mmio_region_write32(lc->base_addr, LC_CTRL_CLAIM_TRANSITION_IF_REG_OFFSET,
284 kMultiBitBool8False);
285 return kDifOk;
286}
287
288dif_result_t dif_lc_ctrl_configure(const dif_lc_ctrl_t *lc,
290 bool use_ext_clock,
291 const dif_lc_ctrl_token_t *token) {
292 if (lc == NULL) {
293 return kDifBadArg;
294 }
295
296 uint32_t target;
297 switch (state) {
299 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_RAW;
300 break;
302 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_TEST_UNLOCKED0;
303 break;
305 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_TEST_LOCKED0;
306 break;
308 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_TEST_UNLOCKED1;
309 break;
311 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_TEST_LOCKED1;
312 break;
314 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_TEST_UNLOCKED2;
315 break;
317 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_TEST_LOCKED2;
318 break;
320 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_TEST_UNLOCKED3;
321 break;
323 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_TEST_LOCKED3;
324 break;
326 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_TEST_UNLOCKED4;
327 break;
329 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_TEST_LOCKED4;
330 break;
332 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_TEST_UNLOCKED5;
333 break;
335 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_TEST_LOCKED5;
336 break;
338 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_TEST_UNLOCKED6;
339 break;
341 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_TEST_LOCKED6;
342 break;
344 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_TEST_UNLOCKED7;
345 break;
347 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_DEV;
348 break;
350 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_PROD;
351 break;
353 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_PROD_END;
354 break;
356 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_RMA;
357 break;
359 target = LC_CTRL_TRANSITION_TARGET_STATE_VALUE_SCRAP;
360 break;
361
362 default:
363 return kDifBadArg;
364 }
365
366 // Check if the mutex has been acquired.
367 if (!mmio_region_read32(lc->base_addr,
368 LC_CTRL_TRANSITION_REGWEN_REG_OFFSET)) {
369 return kDifUnavailable;
370 }
371
372 // Set the target for the transition.
373 mmio_region_write32(lc->base_addr, LC_CTRL_TRANSITION_TARGET_REG_OFFSET,
374 target);
375
376 // Program the clock selection.
377 uint32_t ctrl_reg = 0;
378 if (use_ext_clock) {
379 ctrl_reg = bitfield_bit32_write(
380 ctrl_reg, LC_CTRL_TRANSITION_CTRL_EXT_CLOCK_EN_BIT, true);
381 } else {
382 // Default to internal clock.
383 ctrl_reg = bitfield_bit32_write(
384 ctrl_reg, LC_CTRL_TRANSITION_CTRL_EXT_CLOCK_EN_BIT, false);
385 }
386 mmio_region_write32(lc->base_addr, LC_CTRL_TRANSITION_CTRL_REG_OFFSET,
387 ctrl_reg);
388
389 // Fill in a token, if necessary.
390 if (token != NULL) {
391 for (int i = 0; i < sizeof(token->data); i += sizeof(uint32_t)) {
392 uint32_t word;
393 memcpy(&word, &token->data[i], sizeof(uint32_t));
394 mmio_region_write32(lc->base_addr,
395 LC_CTRL_TRANSITION_TOKEN_0_REG_OFFSET + i, word);
396 }
397 }
398
399 return kDifOk;
400}
401
402dif_result_t dif_lc_ctrl_transition(const dif_lc_ctrl_t *lc) {
403 if (lc == NULL) {
404 return kDifBadArg;
405 }
406
407 // Check if the mutex has been acquired.
408 if (!mmio_region_read32(lc->base_addr,
409 LC_CTRL_TRANSITION_REGWEN_REG_OFFSET)) {
410 return kDifUnavailable;
411 }
412
413 mmio_region_write32(lc->base_addr, LC_CTRL_TRANSITION_CMD_REG_OFFSET, 1);
414
415 return kDifOk;
416}
417
418dif_result_t dif_lc_ctrl_set_otp_vendor_test_reg(const dif_lc_ctrl_t *lc,
419 uint32_t settings) {
420 if (lc == NULL) {
421 return kDifBadArg;
422 }
423
424 uint32_t busy =
425 mmio_region_read32(lc->base_addr, LC_CTRL_TRANSITION_REGWEN_REG_OFFSET);
426 if (busy == 0) {
427 return kDifUnavailable;
428 }
429
430 mmio_region_write32(lc->base_addr, LC_CTRL_OTP_VENDOR_TEST_CTRL_REG_OFFSET,
431 settings);
432
433 return kDifOk;
434}
435
436dif_result_t dif_lc_ctrl_get_otp_vendor_test_reg(const dif_lc_ctrl_t *lc,
437 uint32_t *settings) {
438 if (lc == NULL || settings == NULL) {
439 return kDifBadArg;
440 }
441
442 uint32_t busy =
443 mmio_region_read32(lc->base_addr, LC_CTRL_TRANSITION_REGWEN_REG_OFFSET);
444 if (busy == 0) {
445 return kDifUnavailable;
446 }
447
448 *settings = mmio_region_read32(lc->base_addr,
449 LC_CTRL_OTP_VENDOR_TEST_CTRL_REG_OFFSET);
450
451 return kDifOk;
452}
453
454dif_result_t dif_lc_ctrl_sw_mutex_lock(const dif_lc_ctrl_t *lc) {
455 if (lc == NULL) {
456 return kDifBadArg;
457 }
458
459 // Clear CLAIM_TRANSITION_IF_REGWEN to lock mutex claim from TL-UL side.
460 mmio_region_write32(lc->base_addr,
461 LC_CTRL_CLAIM_TRANSITION_IF_REGWEN_REG_OFFSET, 0);
462
463 return kDifOk;
464}