1 /*
2 * Copyright (c) 2022-2023, Arm Limited. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /**
18 * \file lcm_drv.c
19 * \brief Driver for Arm LCM.
20 */
21
22 #include "lcm_drv.h"
23 #include <stddef.h>
24 #include <stdint.h>
25
26 static uint8_t dummy_key_value[32] = {0x01, 0x02, 0x03, 0x04,
27 0x01, 0x02, 0x03, 0x04,
28 0x01, 0x02, 0x03, 0x04,
29 0x01, 0x02, 0x03, 0x04,
30 0x01, 0x02, 0x03, 0x04,
31 0x01, 0x02, 0x03, 0x04,
32 0x01, 0x02, 0x03, 0x04,
33 0x01, 0x02, 0x03, 0x04};
34
35 struct _lcm_reg_map_t {
36 volatile uint32_t lcs_value;
37 /*!< Offset: 0x000 (R/ ) LCM Lifecycle state Register */
38 volatile uint32_t key_err;
39 /*!< Offset: 0x004 (R/ ) LCM zero count error status Register */
40 volatile uint32_t tp_mode;
41 /*!< Offset: 0x008 (R/ ) LCM TP Mode (TCI/PCI) Register */
42 volatile uint32_t fatal_err;
43 /*!< Offset: 0x00C (R/W) LCM Fatal Error mode Enable Register */
44 volatile uint32_t dm_rma_lock;
45 /*!< Offset: 0x010 (R/W) LCM DRM RMA Flag lock enable */
46 volatile uint32_t sp_enable;
47 /*!< Offset: 0x014 (R/W) LCM Secure Provisioning enable
48 * Register */
49 volatile uint32_t otp_addr_width;
50 /*!< Offset: 0x018 (R/ ) LCM OTP Address Width Register */
51 volatile uint32_t otp_size_in_bytes;
52 /*!< Offset: 0x01C (R/ ) LCM OTP Size in bytes Register */
53 volatile uint32_t gppc;
54 /*!< Offset: 0x020 (R/ ) LCM General Purpose Persistent
55 * Configuration Register */
56 volatile uint32_t reserved_0[55];
57 /*!< Offset: 0x024-0x0FC Reserved */
58 volatile uint32_t dcu_en[4];
59 /*!< Offset: 0x100 (R/W) LCM DCU enable Registers */
60 volatile uint32_t dcu_lock[4];
61 /*!< Offset: 0x110 (R/W) LCM DCU lock Registers */
62 volatile uint32_t dcu_sp_disable_mask[4];
63 /*!< Offset: 0x120 (R/ ) LCM DCU SP disable mask Registers */
64 volatile uint32_t dcu_disable_mask[4];
65 /*!< Offset: 0x130 (R/ ) LCM DCU disable mask Registers */
66 volatile uint32_t reserved_1[932];
67 /*!< Offset: 0x140-0xFCC Reserved */
68 volatile uint32_t pidr4;
69 /*!< Offset: 0xFD0 (R/ ) Peripheral ID 4 */
70 volatile uint32_t reserved_2[3];
71 /*!< Offset: 0xFD4-0xFDC Reserved */
72 volatile uint32_t pidr0;
73 /*!< Offset: 0xFE0 (R/ ) Peripheral ID 0 */
74 volatile uint32_t pidr1;
75 /*!< Offset: 0xFE4 (R/ ) Peripheral ID 1 */
76 volatile uint32_t pidr2;
77 /*!< Offset: 0xFE8 (R/ ) Peripheral ID 2 */
78 volatile uint32_t pidr3;
79 /*!< Offset: 0xFEC (R/ ) Peripheral ID 3 */
80 volatile uint32_t cidr0;
81 /*!< Offset: 0xFF0 (R/ ) Component ID 0 */
82 volatile uint32_t cidr1;
83 /*!< Offset: 0xFF4 (R/ ) Component ID 1 */
84 volatile uint32_t cidr2;
85 /*!< Offset: 0xFF8 (R/ ) Component ID 2 */
86 volatile uint32_t cidr3;
87 /*!< Offset: 0xFFC (R/ ) Component ID 3 */
88 union {
89 volatile uint32_t raw_otp[16384];
90 /*!< Offset: 0x1000 (R/W) OTP direct access */
91 struct lcm_otp_layout_t otp;
92 };
93 };
94
is_pointer_word_aligned(void * ptr)95 static int is_pointer_word_aligned(void *ptr) {
96 return !((uint32_t)ptr & (sizeof(uint32_t) - 1));
97 }
98
lcm_init(struct lcm_dev_t * dev)99 enum lcm_error_t lcm_init(struct lcm_dev_t *dev)
100 {
101 struct _lcm_reg_map_t *p_lcm = (struct _lcm_reg_map_t *)dev->cfg->base;
102 enum lcm_lcs_t lcs;
103 enum lcm_error_t err;
104
105 err = lcm_get_lcs(dev, &lcs);
106 if (err != LCM_ERROR_NONE) {
107 return err;
108 }
109
110 if (lcs == LCM_LCS_SE) {
111 if (p_lcm->key_err) {
112 return LCM_ERROR_INVALID_KEY;
113 }
114 }
115
116 return LCM_ERROR_NONE;
117 }
118
lcm_get_tp_mode(struct lcm_dev_t * dev,enum lcm_tp_mode_t * mode)119 enum lcm_error_t lcm_get_tp_mode(struct lcm_dev_t *dev, enum lcm_tp_mode_t *mode)
120 {
121 struct _lcm_reg_map_t *p_lcm = (struct _lcm_reg_map_t *)dev->cfg->base;
122
123 *mode = (enum lcm_tp_mode_t)p_lcm->tp_mode;
124
125 return LCM_ERROR_NONE;
126 }
127
lcm_set_tp_mode(struct lcm_dev_t * dev,enum lcm_tp_mode_t mode)128 enum lcm_error_t lcm_set_tp_mode(struct lcm_dev_t *dev, enum lcm_tp_mode_t mode)
129 {
130 enum lcm_tp_mode_t curr_mode;
131 enum lcm_lcs_t lcs;
132 uint32_t mode_reg_value;
133 uint32_t readback_reg_value;
134 enum lcm_bool_t fatal_err;
135 enum lcm_error_t err;
136
137 err = lcm_get_lcs(dev, &lcs);
138 if (err != LCM_ERROR_NONE) {
139 return err;
140 }
141
142 if (lcs != LCM_LCS_CM) {
143 return LCM_ERROR_INVALID_LCS;
144 }
145
146 err = lcm_get_tp_mode(dev, &curr_mode);
147 if (err != LCM_ERROR_NONE) {
148 return err;
149 }
150
151 if(curr_mode != LCM_TP_MODE_VIRGIN) {
152 return LCM_ERROR_INVALID_TRANSITION;
153 }
154
155 switch(mode) {
156 case LCM_TP_MODE_TCI:
157 /* High hamming-weight magic constant used to enable TCI mode */
158 mode_reg_value = 0x0000FFFFu;
159 break;
160 case LCM_TP_MODE_PCI:
161 /* High hamming-weight magic constant used to enable PCI mode */
162 mode_reg_value = 0xFFFF0000u;
163 break;
164 default:
165 return LCM_ERROR_INVALID_TRANSITION;
166 }
167
168 err = lcm_otp_write(dev, offsetof(struct lcm_otp_layout_t, tp_mode_config),
169 sizeof(uint32_t), (uint8_t *)&mode_reg_value);
170 if (err != LCM_ERROR_NONE) {
171 return err;
172 }
173
174 err = lcm_otp_read(dev, offsetof(struct lcm_otp_layout_t, tp_mode_config),
175 sizeof(uint32_t), (uint8_t *)&readback_reg_value);
176 if (err != LCM_ERROR_NONE) {
177 return err;
178 }
179
180 if (readback_reg_value != mode_reg_value) {
181 return LCM_ERROR_INTERNAL_ERROR;
182 }
183
184 err = lcm_get_fatal_error(dev, &fatal_err);
185 if (err != LCM_ERROR_NONE) {
186 return err;
187 }
188
189 if (fatal_err == LCM_TRUE) {
190 return LCM_ERROR_FATAL_ERR;
191 }
192
193 return LCM_ERROR_NONE;
194 }
195
lcm_get_sp_enabled(struct lcm_dev_t * dev,enum lcm_bool_t * enabled)196 enum lcm_error_t lcm_get_sp_enabled(struct lcm_dev_t *dev, enum lcm_bool_t *enabled)
197 {
198 struct _lcm_reg_map_t *p_lcm = (struct _lcm_reg_map_t *)dev->cfg->base;
199
200 *enabled = (enum lcm_bool_t)p_lcm->sp_enable;
201
202 return LCM_ERROR_NONE;
203 }
204
lcm_set_sp_enabled(struct lcm_dev_t * dev)205 enum lcm_error_t lcm_set_sp_enabled(struct lcm_dev_t *dev)
206 {
207 struct _lcm_reg_map_t *p_lcm = (struct _lcm_reg_map_t *)dev->cfg->base;
208 enum lcm_bool_t fatal_err;
209 enum lcm_error_t err;
210
211 /* High hamming-weight magic constant used to trigger secure provisioning
212 * mode
213 */
214 p_lcm->sp_enable = 0x5EC10E1Eu;
215
216 while(p_lcm->sp_enable != LCM_TRUE) {}
217
218 err = lcm_get_fatal_error(dev, &fatal_err);
219 if (err != LCM_ERROR_NONE) {
220 return err;
221 }
222
223 if (fatal_err == LCM_TRUE) {
224 return LCM_ERROR_FATAL_ERR;
225 }
226
227 return LCM_ERROR_NONE;
228 }
229
lcm_get_fatal_error(struct lcm_dev_t * dev,enum lcm_bool_t * error)230 enum lcm_error_t lcm_get_fatal_error(struct lcm_dev_t *dev, enum lcm_bool_t *error)
231 {
232 struct _lcm_reg_map_t *p_lcm = (struct _lcm_reg_map_t *)dev->cfg->base;
233
234 *error = (enum lcm_bool_t)p_lcm->fatal_err;
235
236 return LCM_ERROR_NONE;
237 }
238
lcm_set_fatal_error(struct lcm_dev_t * dev)239 enum lcm_error_t lcm_set_fatal_error(struct lcm_dev_t *dev)
240 {
241 struct _lcm_reg_map_t *p_lcm = (struct _lcm_reg_map_t *)dev->cfg->base;
242
243 /* High hamming-weight magic constant used to trigger fatal error state */
244 p_lcm->fatal_err = 0xFA7A1EEEu;
245
246 return LCM_ERROR_NONE;
247 }
248
lcm_get_gppc(struct lcm_dev_t * dev,uint32_t * gppc)249 enum lcm_error_t lcm_get_gppc(struct lcm_dev_t *dev, uint32_t *gppc)
250 {
251 struct _lcm_reg_map_t *p_lcm = (struct _lcm_reg_map_t *)dev->cfg->base;
252
253 *gppc = p_lcm->gppc;
254
255 return LCM_ERROR_NONE;
256 }
257
lcm_get_otp_size(struct lcm_dev_t * dev,uint32_t * size)258 enum lcm_error_t lcm_get_otp_size(struct lcm_dev_t *dev, uint32_t *size)
259 {
260 struct _lcm_reg_map_t *p_lcm = (struct _lcm_reg_map_t *)dev->cfg->base;
261
262 *size = p_lcm->otp_size_in_bytes;
263
264 return LCM_ERROR_NONE;
265 }
266
lcm_get_lcs(struct lcm_dev_t * dev,enum lcm_lcs_t * lcs)267 enum lcm_error_t lcm_get_lcs(struct lcm_dev_t *dev, enum lcm_lcs_t *lcs)
268 {
269 struct _lcm_reg_map_t *p_lcm = (struct _lcm_reg_map_t *)dev->cfg->base;
270 enum lcm_bool_t fatal_err;
271 enum lcm_error_t err;
272
273 err = lcm_get_fatal_error(dev, &fatal_err);
274 if (err != LCM_ERROR_NONE) {
275 return err;
276 }
277
278 if (fatal_err == LCM_TRUE) {
279 return LCM_ERROR_FATAL_ERR;
280 }
281
282
283 *lcs = (enum lcm_lcs_t)p_lcm->lcs_value;
284
285 if (*lcs == LCM_LCS_INVALID) {
286 return LCM_ERROR_INVALID_LCS;
287 }
288
289 return LCM_ERROR_NONE;
290 }
291
292 /* Armclang attempts to inline this function, which causes huge code size
293 * increases. It is marked as __attribute__((noinline)) explicitly to prevent
294 * this.
295 */
296 __attribute__((noinline))
count_otp_zero_bits(struct lcm_dev_t * dev,uint32_t offset,uint32_t len,uint32_t * zero_bits)297 static enum lcm_error_t count_otp_zero_bits(struct lcm_dev_t *dev,
298 uint32_t offset, uint32_t len,
299 uint32_t *zero_bits)
300 {
301 enum lcm_error_t err;
302 uint32_t idx;
303 uint32_t word;
304 uint32_t bit_index;
305
306 *zero_bits = 0;
307
308 for (idx = 0; idx < len; idx += sizeof(uint32_t)) {
309 err = lcm_otp_read(dev, offset + idx, sizeof(uint32_t), (uint8_t *)&word);
310 if (err != LCM_ERROR_NONE) {
311 return err;
312 }
313
314 for (bit_index = 0; bit_index < sizeof(word) * 8; bit_index++) {
315 *zero_bits += 1 - ((word >> bit_index) & 1);
316 }
317 }
318
319 return LCM_ERROR_NONE;
320 }
321
cm_to_dm(struct lcm_dev_t * dev,uint16_t gppc_val)322 static enum lcm_error_t cm_to_dm(struct lcm_dev_t *dev, uint16_t gppc_val)
323 {
324 enum lcm_error_t err;
325 uint32_t config_val;
326 uint32_t zero_bits;
327
328 config_val = LCM_TRUE;
329
330 err = count_otp_zero_bits(dev, offsetof(struct lcm_otp_layout_t, huk), 32,
331 &zero_bits);
332 if (err != LCM_ERROR_NONE) {
333 return err;
334 }
335 if (zero_bits == 256) {
336 err = lcm_otp_write(dev, offsetof(struct lcm_otp_layout_t, huk), 32,
337 dummy_key_value);
338 if (err != LCM_ERROR_NONE) {
339 return err;
340 }
341 }
342
343 err = count_otp_zero_bits(dev, offsetof(struct lcm_otp_layout_t, guk), 32,
344 &zero_bits);
345 if (err != LCM_ERROR_NONE) {
346 return err;
347 }
348 if (zero_bits == 256) {
349 err = lcm_otp_write(dev, offsetof(struct lcm_otp_layout_t, guk), 32,
350 dummy_key_value);
351 if (err != LCM_ERROR_NONE) {
352 return err;
353 }
354 }
355
356 err = count_otp_zero_bits(dev, offsetof(struct lcm_otp_layout_t, kp_cm), 32,
357 &zero_bits);
358 if (err != LCM_ERROR_NONE) {
359 return err;
360 }
361 if (zero_bits == 256) {
362 err = lcm_otp_write(dev, offsetof(struct lcm_otp_layout_t, kp_cm), 32,
363 dummy_key_value);
364 if (err != LCM_ERROR_NONE) {
365 return err;
366 }
367 }
368
369 err = count_otp_zero_bits(dev, offsetof(struct lcm_otp_layout_t, kce_cm), 32,
370 &zero_bits);
371 if (err != LCM_ERROR_NONE) {
372 return err;
373 }
374 if (zero_bits == 256) {
375 err = lcm_otp_write(dev, offsetof(struct lcm_otp_layout_t, kce_cm), 32,
376 dummy_key_value);
377 if (err != LCM_ERROR_NONE) {
378 return err;
379 }
380 }
381
382 err = lcm_otp_write(dev, offsetof(struct lcm_otp_layout_t, cm_config_1),
383 sizeof(uint32_t), (uint8_t *)&config_val);
384 /* This OTP field doesn't read-back as written, but that isn't an error */
385 if (!(err == LCM_ERROR_NONE || err == LCM_ERROR_WRITE_VERIFY_FAIL)) {
386 return err;
387 }
388
389 err = lcm_otp_read(dev, offsetof(struct lcm_otp_layout_t, cm_config_1),
390 sizeof(uint32_t), (uint8_t *)&config_val);
391 if (err != LCM_ERROR_NONE) {
392 return err;
393 }
394
395 if (config_val != 0) {
396 return LCM_ERROR_WRITE_VERIFY_FAIL;
397 }
398
399 config_val = 0;
400
401 err = count_otp_zero_bits(dev, offsetof(struct lcm_otp_layout_t, rotpk),
402 32, &zero_bits);
403 if (err != LCM_ERROR_NONE) {
404 return err;
405 }
406
407 config_val |= (zero_bits & 0xFF) << 0;
408 config_val |= ((uint32_t)gppc_val) << 8;
409
410 err = lcm_otp_write(dev, offsetof(struct lcm_otp_layout_t, cm_config_2),
411 sizeof(uint32_t), (uint8_t *)&config_val);
412 if (err != LCM_ERROR_NONE) {
413 return err;
414 }
415
416 return LCM_ERROR_NONE;
417 }
418
dm_to_se(struct lcm_dev_t * dev)419 static enum lcm_error_t dm_to_se(struct lcm_dev_t *dev)
420 {
421 enum lcm_error_t err;
422 uint32_t config_val;
423 uint32_t zero_bits;
424
425 err = count_otp_zero_bits(dev, offsetof(struct lcm_otp_layout_t, kp_dm), 32,
426 &zero_bits);
427 if (err != LCM_ERROR_NONE) {
428 return err;
429 }
430 if (zero_bits == 256) {
431 err = lcm_otp_write(dev, offsetof(struct lcm_otp_layout_t, kp_dm), 32,
432 dummy_key_value);
433 if (err != LCM_ERROR_NONE) {
434 return err;
435 }
436 }
437
438 err = count_otp_zero_bits(dev, offsetof(struct lcm_otp_layout_t, kce_dm), 32,
439 &zero_bits);
440 if (err != LCM_ERROR_NONE) {
441 return err;
442 }
443 if (zero_bits == 256) {
444 err = lcm_otp_write(dev, offsetof(struct lcm_otp_layout_t, kce_dm), 32,
445 dummy_key_value);
446 if (err != LCM_ERROR_NONE) {
447 return err;
448 }
449 }
450
451 config_val = LCM_TRUE;
452
453 /* This OTP field doesn't read-back as written, but that isn't an error */
454 err = lcm_otp_write(dev, offsetof(struct lcm_otp_layout_t, dm_config),
455 sizeof(uint32_t), (uint8_t *)&config_val);
456 if (!(err == LCM_ERROR_NONE || err == LCM_ERROR_WRITE_VERIFY_FAIL)) {
457 return err;
458 }
459
460 /* Manually check that the readback value is what we expect (0x0 means no
461 * key bit count errors).
462 */
463 err = lcm_otp_read(dev, offsetof(struct lcm_otp_layout_t, dm_config),
464 sizeof(uint32_t), (uint8_t *)&config_val);
465 if (err != LCM_ERROR_NONE) {
466 return err;
467 }
468
469 if (config_val != 0) {
470 return LCM_ERROR_WRITE_VERIFY_FAIL;
471 }
472
473 return LCM_ERROR_NONE;
474 }
475
se_to_rma(struct lcm_dev_t * dev)476 static enum lcm_error_t se_to_rma(struct lcm_dev_t *dev)
477 {
478 enum lcm_error_t err;
479 uint32_t rma_flag = LCM_TRUE;
480 uint32_t idx;
481 uint32_t otp_overwrite_val = 0xFFFFFFFFu;
482
483 for (idx = 0; idx < offsetof(struct lcm_otp_layout_t, tp_mode_config);
484 idx += sizeof(uint32_t)) {
485 err = lcm_otp_write(dev, idx, sizeof(otp_overwrite_val),
486 (uint8_t *)&otp_overwrite_val);
487 if (err != LCM_ERROR_NONE) {
488 return err;
489 }
490 }
491
492 err = lcm_otp_write(dev, offsetof(struct lcm_otp_layout_t, cm_rma_flag),
493 sizeof(uint32_t), (uint8_t *)&rma_flag);
494 if (err != LCM_ERROR_NONE) {
495 return err;
496 }
497
498 err = lcm_otp_write(dev, offsetof(struct lcm_otp_layout_t, dm_rma_flag),
499 sizeof(uint32_t), (uint8_t *)&rma_flag);
500 if (err != LCM_ERROR_NONE) {
501 return err;
502 }
503
504 return LCM_ERROR_NONE;
505 }
506
lcm_set_lcs(struct lcm_dev_t * dev,enum lcm_lcs_t lcs,uint16_t gppc_val)507 enum lcm_error_t lcm_set_lcs(struct lcm_dev_t *dev, enum lcm_lcs_t lcs,
508 uint16_t gppc_val)
509 {
510 enum lcm_bool_t fatal_err;
511 enum lcm_bool_t sp_enable;
512 enum lcm_tp_mode_t tp_mode;
513 enum lcm_error_t err;
514 enum lcm_lcs_t curr_lcs;
515
516 err = lcm_get_lcs(dev, &curr_lcs);
517 if (err != LCM_ERROR_NONE) {
518 return err;
519 }
520 if (lcs == curr_lcs) {
521 return LCM_ERROR_NONE;
522 }
523
524 err = lcm_get_tp_mode(dev, &tp_mode);
525 if (err != LCM_ERROR_NONE) {
526 return err;
527 }
528 if (!(tp_mode == LCM_TP_MODE_PCI || tp_mode == LCM_TP_MODE_TCI)) {
529 return LCM_ERROR_INVALID_TP_MODE;
530 }
531
532 err = lcm_get_sp_enabled(dev, &sp_enable);
533 if (err != LCM_ERROR_NONE) {
534 return err;
535 }
536 if (sp_enable != LCM_TRUE) {
537 err = lcm_set_sp_enabled(dev);
538 if (err != LCM_ERROR_NONE) {
539 return err;
540 }
541 }
542
543 do {
544 err = lcm_get_sp_enabled(dev, &sp_enable);
545 if (err != LCM_ERROR_NONE) {
546 return err;
547 }
548 err = lcm_get_fatal_error(dev, &fatal_err);
549 if (err != LCM_ERROR_NONE) {
550 return err;
551 }
552 } while (sp_enable == LCM_FALSE && fatal_err == LCM_FALSE);
553
554 if (fatal_err == LCM_TRUE) {
555 return LCM_ERROR_FATAL_ERR;
556 }
557
558 switch (lcs) {
559 case LCM_LCS_CM:
560 /* There's no possible valid transition back to CM */
561 return LCM_ERROR_INVALID_TRANSITION;
562 case LCM_LCS_DM:
563 if (curr_lcs != LCM_LCS_CM) {
564 return LCM_ERROR_INVALID_TRANSITION;
565 }
566
567 return cm_to_dm(dev, gppc_val);
568
569 case LCM_LCS_SE:
570 if (curr_lcs != LCM_LCS_DM) {
571 return LCM_ERROR_INVALID_TRANSITION;
572 }
573
574 return dm_to_se(dev);
575
576 case LCM_LCS_RMA:
577 if (curr_lcs != LCM_LCS_SE) {
578 return LCM_ERROR_INVALID_TRANSITION;
579 }
580
581 return se_to_rma(dev);
582
583 case LCM_LCS_INVALID:
584 return LCM_ERROR_INVALID_LCS;
585 }
586
587 /* Should never get here */
588 return LCM_ERROR_INTERNAL_ERROR;
589 }
590
591
592 /* Armclang attempts to inline this function, which causes huge code size
593 * increases. It is marked as __attribute__((noinline)) explicitly to prevent
594 * this.
595 */
596 __attribute__((noinline))
lcm_otp_write(struct lcm_dev_t * dev,uint32_t offset,uint32_t len,const uint8_t * buf)597 enum lcm_error_t lcm_otp_write(struct lcm_dev_t *dev, uint32_t offset, uint32_t len,
598 const uint8_t *buf)
599 {
600 enum lcm_error_t err;
601 struct _lcm_reg_map_t *p_lcm = (struct _lcm_reg_map_t *)dev->cfg->base;
602 uint32_t *p_buf_word = (uint32_t *)buf;
603 uint32_t otp_size;
604 uint32_t idx;
605
606 if (!is_pointer_word_aligned(p_buf_word)) {
607 return LCM_ERROR_INVALID_ALIGNMENT;
608 }
609
610 if (offset & (sizeof(uint32_t) - 1)) {
611 return LCM_ERROR_INVALID_OFFSET;
612 }
613
614 if (len & (sizeof(uint32_t) - 1)) {
615 return LCM_ERROR_INVALID_LENGTH;
616 }
617
618 err = lcm_get_otp_size(dev, &otp_size);
619 if (err) {
620 return err;
621 }
622
623 if (otp_size < (offset + len)) {
624 return LCM_ERROR_INVALID_OFFSET;
625 }
626
627 /* Reject the write if we'd have to unset a bit. */
628 for (idx = 0; idx < len / sizeof(uint32_t); idx++) {
629 if (p_buf_word[idx] !=
630 (p_lcm->raw_otp[(offset / sizeof(uint32_t)) + idx] | p_buf_word[idx])) {
631 return LCM_ERROR_INVALID_WRITE;
632 }
633 }
634
635 for (idx = 0; idx < len / sizeof(uint32_t); idx++) {
636 p_lcm->raw_otp[(offset / sizeof(uint32_t)) + idx] = p_buf_word[idx];
637 }
638
639 for (idx = 0; idx < len / sizeof(uint32_t); idx++) {
640 if (p_buf_word[idx] != p_lcm->raw_otp[(offset / sizeof(uint32_t)) + idx]) {
641 return LCM_ERROR_WRITE_VERIFY_FAIL;
642 }
643 }
644
645 return LCM_ERROR_NONE;
646 }
647
648
649 /* Armclang attempts to inline this function, which causes huge code size
650 * increases. It is marked as __attribute__((noinline)) explicitly to prevent
651 * this.
652 */
653 __attribute__((noinline))
lcm_otp_read(struct lcm_dev_t * dev,uint32_t offset,uint32_t len,uint8_t * buf)654 enum lcm_error_t lcm_otp_read(struct lcm_dev_t *dev, uint32_t offset,
655 uint32_t len, uint8_t *buf)
656 {
657 enum lcm_error_t err;
658 struct _lcm_reg_map_t *p_lcm = (struct _lcm_reg_map_t *)dev->cfg->base;
659 uint32_t *p_buf_word = (uint32_t *)buf;
660 uint32_t otp_size;
661 uint32_t idx;
662
663 if (!is_pointer_word_aligned(p_buf_word)) {
664 return LCM_ERROR_INVALID_ALIGNMENT;
665 }
666
667 if (offset & (sizeof(uint32_t) - 1)) {
668 return LCM_ERROR_INVALID_OFFSET;
669 }
670
671 if (len & (sizeof(uint32_t) - 1)) {
672 return LCM_ERROR_INVALID_LENGTH;
673 }
674
675 err = lcm_get_otp_size(dev, &otp_size);
676 if (err) {
677 return err;
678 }
679
680 if (otp_size < (offset + len)) {
681 return LCM_ERROR_INVALID_OFFSET;
682 }
683
684 for (idx = 0; idx < len / sizeof(uint32_t); idx++) {
685 p_buf_word[idx] = p_lcm->raw_otp[(offset / sizeof(uint32_t)) + idx];
686 }
687
688 return LCM_ERROR_NONE;
689 }
690
lcm_dcu_get_enabled(struct lcm_dev_t * dev,uint8_t * val)691 enum lcm_error_t lcm_dcu_get_enabled(struct lcm_dev_t *dev, uint8_t *val)
692 {
693 struct _lcm_reg_map_t *p_lcm = (struct _lcm_reg_map_t *)dev->cfg->base;
694 uint32_t *p_val_word = (uint32_t *)val;
695 uint32_t idx;
696
697 if (!is_pointer_word_aligned(p_val_word)) {
698 return LCM_ERROR_INVALID_ALIGNMENT;
699 }
700
701 for (idx = 0; idx < LCM_DCU_WIDTH_IN_BYTES / sizeof(uint32_t); idx++) {
702 p_val_word[idx] = p_lcm->dcu_en[idx];
703 }
704
705 return LCM_ERROR_NONE;
706 }
707
lcm_dcu_set_enabled(struct lcm_dev_t * dev,uint8_t * val)708 enum lcm_error_t lcm_dcu_set_enabled(struct lcm_dev_t *dev, uint8_t *val)
709 {
710 struct _lcm_reg_map_t *p_lcm = (struct _lcm_reg_map_t *)dev->cfg->base;
711 uint32_t *p_val_word = (uint32_t *)val;
712 uint32_t idx;
713
714 if (!is_pointer_word_aligned(p_val_word)) {
715 return LCM_ERROR_INVALID_ALIGNMENT;
716 }
717
718 for (idx = 0; idx < LCM_DCU_WIDTH_IN_BYTES / sizeof(uint32_t); idx++) {
719 p_lcm->dcu_en[idx] = p_val_word[idx];
720 }
721
722 for (idx = 0; idx < LCM_DCU_WIDTH_IN_BYTES / sizeof(uint32_t); idx++) {
723 if (p_lcm->dcu_en[idx] != p_val_word[idx]) {
724 return LCM_ERROR_WRITE_VERIFY_FAIL;
725 }
726 }
727
728 return LCM_ERROR_NONE;
729 }
730
lcm_dcu_get_locked(struct lcm_dev_t * dev,uint8_t * val)731 enum lcm_error_t lcm_dcu_get_locked(struct lcm_dev_t *dev, uint8_t *val)
732 {
733 struct _lcm_reg_map_t *p_lcm = (struct _lcm_reg_map_t *)dev->cfg->base;
734 uint32_t *p_val_word = (uint32_t *)val;
735 uint32_t idx;
736
737 if (!is_pointer_word_aligned(p_val_word)) {
738 return LCM_ERROR_INVALID_ALIGNMENT;
739 }
740
741 for (idx = 0; idx < LCM_DCU_WIDTH_IN_BYTES / sizeof(uint32_t); idx++) {
742 p_val_word[idx] = p_lcm->dcu_lock[idx];
743 }
744
745 return LCM_ERROR_NONE;
746 }
747
lcm_dcu_set_locked(struct lcm_dev_t * dev,uint8_t * val)748 enum lcm_error_t lcm_dcu_set_locked(struct lcm_dev_t *dev, uint8_t *val)
749 {
750 struct _lcm_reg_map_t *p_lcm = (struct _lcm_reg_map_t *)dev->cfg->base;
751 uint32_t *p_val_word = (uint32_t *)val;
752 uint32_t idx;
753
754 if (!is_pointer_word_aligned(p_val_word)) {
755 return LCM_ERROR_INVALID_ALIGNMENT;
756 }
757
758 for (idx = 0; idx < LCM_DCU_WIDTH_IN_BYTES / sizeof(uint32_t); idx++) {
759 p_lcm->dcu_lock[idx] = p_val_word[idx];
760 }
761
762 return LCM_ERROR_NONE;
763 }
764
lcm_dcu_get_sp_disable_mask(struct lcm_dev_t * dev,uint8_t * val)765 enum lcm_error_t lcm_dcu_get_sp_disable_mask(struct lcm_dev_t *dev, uint8_t *val)
766 {
767 struct _lcm_reg_map_t *p_lcm = (struct _lcm_reg_map_t *)dev->cfg->base;
768 uint32_t *p_val_word = (uint32_t *)val;
769 uint32_t idx;
770
771 if (!is_pointer_word_aligned(p_val_word)) {
772 return LCM_ERROR_INVALID_ALIGNMENT;
773 }
774
775 for (idx = 0; idx < LCM_DCU_WIDTH_IN_BYTES / sizeof(uint32_t); idx++) {
776 p_val_word[idx] = p_lcm->dcu_sp_disable_mask[idx];
777 }
778
779 return LCM_ERROR_NONE;
780 }
781
lcm_dcu_get_disable_mask(struct lcm_dev_t * dev,uint8_t * val)782 enum lcm_error_t lcm_dcu_get_disable_mask(struct lcm_dev_t *dev, uint8_t *val)
783 {
784 struct _lcm_reg_map_t *p_lcm = (struct _lcm_reg_map_t *)dev->cfg->base;
785 uint32_t *p_val_word = (uint32_t *)val;
786 uint32_t idx;
787
788 if (!is_pointer_word_aligned(p_val_word)) {
789 return LCM_ERROR_INVALID_ALIGNMENT;
790 }
791
792 for (idx = 0; idx < LCM_DCU_WIDTH_IN_BYTES / sizeof(uint32_t); idx++) {
793 p_val_word[idx] = p_lcm->dcu_disable_mask[idx];
794 }
795
796 return LCM_ERROR_NONE;
797 }
798
799