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