1 /******************************************************************************
2  *
3  * Copyright (C) 2022-2023 Maxim Integrated Products, Inc. (now owned by
4  * Analog Devices, Inc.),
5  * Copyright (C) 2023-2024 Analog Devices, Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  ******************************************************************************/
20 
21 /**
22  * @file mxc_sys.c
23  * @brief      System layer driver.
24  * @details    This driver is used to control the system layer of the device.
25  */
26 
27 /* **** Includes **** */
28 #include <stddef.h>
29 #include <string.h>
30 #include "mxc_device.h"
31 #include "mxc_assert.h"
32 #include "mxc_sys.h"
33 #include "mxc_delay.h"
34 #include "lpgcr_regs.h"
35 #include "gcr_regs.h"
36 #include "fcr_regs.h"
37 #include "mcr_regs.h"
38 #include "pwrseq_regs.h"
39 #include "flc.h"
40 #include "ctb.h"
41 
42 /**
43  * @ingroup mxc_sys
44  * @{
45  */
46 
47 /* **** Definitions **** */
48 #define MXC_SYS_CLOCK_TIMEOUT MSEC(1)
49 
50 // DAP Lock macros
51 #define INFOBLOCK_DAP_LOCK_OFFSET 0x30
52 #define DAP_LOCK_SEQUENCE_01 0x5A5AA5A5
53 #define DAP_LOCK_SEQUENCE_23 0xFFFFFFFF
54 
55 /* **** Globals **** */
56 
57 /* Symbol defined when loading RISCV image */
58 extern uint32_t _binary_riscv_bin_start;
59 
60 /* **** Functions **** */
61 
62 /* ************************************************************************** */
MXC_SYS_GetUSN(uint8_t * usn,uint8_t * checksum)63 int MXC_SYS_GetUSN(uint8_t *usn, uint8_t *checksum)
64 {
65     if (usn == NULL) {
66         return E_NULL_PTR;
67     }
68 
69     uint32_t *infoblock = (uint32_t *)MXC_INFO0_MEM_BASE;
70 
71     /* Read the USN from the info block */
72     MXC_FLC_UnlockInfoBlock(MXC_INFO0_MEM_BASE);
73 
74     memset(usn, 0, MXC_SYS_USN_CHECKSUM_LEN);
75 
76     usn[0] = (infoblock[0] & 0x007F8000) >> 15;
77     usn[1] = (infoblock[0] & 0x7F800000) >> 23;
78     usn[2] = (infoblock[1] & 0x0000007F) << 1;
79     usn[2] |= (infoblock[0] & 0x80000000) >> 31;
80     usn[3] = (infoblock[1] & 0x00007F80) >> 7;
81     usn[4] = (infoblock[1] & 0x007F8000) >> 15;
82     usn[5] = (infoblock[1] & 0x7F800000) >> 23;
83     usn[6] = (infoblock[2] & 0x007F8000) >> 15;
84     usn[7] = (infoblock[2] & 0x7F800000) >> 23;
85     usn[8] = (infoblock[3] & 0x0000007F) << 1;
86     usn[8] |= (infoblock[2] & 0x80000000) >> 31;
87     usn[9] = (infoblock[3] & 0x00007F80) >> 7;
88     usn[10] = (infoblock[3] & 0x007F8000) >> 15;
89 
90     // Compute the checksum
91     if (checksum != NULL) {
92         uint32_t key[4];
93         uint32_t pt32[4];
94         uint32_t check_csum32[4];
95         uint8_t check_csum[MXC_SYS_USN_CHECKSUM_LEN];
96 
97         /* Initialize key and plaintext */
98         memset(key, 0, MXC_SYS_USN_CHECKSUM_LEN);
99         memset(pt32, 0, MXC_SYS_USN_CHECKSUM_LEN);
100         memcpy(pt32, usn, MXC_SYS_USN_CHECKSUM_LEN);
101 
102         /* Read the checksum from the info block */
103         checksum[1] = ((infoblock[3] & 0x7F800000) >> 23);
104         checksum[0] = ((infoblock[4] & 0x007F8000) >> 15);
105 
106         MXC_CTB_Init(MXC_CTB_FEATURE_CIPHER);
107 
108         /* Reset the CTB */
109         MXC_CTB->ctrl = MXC_F_CTB_CTRL_RST;
110 
111         /* Set the legacy bit */
112         MXC_CTB->ctrl |= MXC_F_CTB_CTRL_FLAG_MODE;
113 
114         /* Clear interrupt flags */
115         MXC_CTB->ctrl |= MXC_F_CTB_CTRL_CPH_DONE;
116 
117         /* Setup the key source */
118         MXC_CTB->cipher_ctrl = MXC_S_CTB_CIPHER_CTRL_SRC_CIPHERKEY;
119 
120         /* Setup the CT calculation */
121         MXC_CTB->cipher_ctrl |= MXC_S_CTB_CIPHER_CTRL_CIPHER_AES128;
122 
123         /* Load the key */
124         MXC_CTB->cipher_key[0] = key[0];
125         MXC_CTB->cipher_key[1] = key[1];
126         MXC_CTB->cipher_key[2] = key[2];
127         MXC_CTB->cipher_key[3] = key[3];
128 
129         /* Wait for the ready flag */
130         while (!(MXC_CTB->ctrl & MXC_F_CTB_CTRL_RDY)) {}
131 
132         /* Copy data to start the operation */
133         MXC_CTB->din[0] = pt32[0];
134         MXC_CTB->din[1] = pt32[1];
135         MXC_CTB->din[2] = pt32[2];
136         MXC_CTB->din[3] = pt32[3];
137 
138         /* Wait for and clear the done flag */
139         while (!(MXC_CTB->ctrl & MXC_F_CTB_CTRL_CPH_DONE)) {}
140         MXC_CTB->ctrl |= MXC_F_CTB_CTRL_CPH_DONE;
141 
142         /* Copy out the cipher text */
143         check_csum32[0] = MXC_CTB->dout[0];
144         check_csum32[1] = MXC_CTB->dout[1];
145         check_csum32[2] = MXC_CTB->dout[2];
146         check_csum32[3] = MXC_CTB->dout[3];
147 
148         memcpy(check_csum, check_csum32, MXC_SYS_USN_CHECKSUM_LEN);
149 
150         /* Verify the checksum */
151         if ((checksum[0] != check_csum[0]) || (checksum[1] != check_csum[1])) {
152             MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
153             return E_UNKNOWN;
154         }
155     }
156 
157     /* Add the info block checksum to the USN */
158     usn[11] = ((infoblock[3] & 0x7F800000) >> 23);
159     usn[12] = ((infoblock[4] & 0x007F8000) >> 15);
160 
161     MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
162 
163     return E_NO_ERROR;
164 }
165 
166 /* ************************************************************************** */
MXC_SYS_IsClockEnabled(mxc_sys_periph_clock_t clock)167 int MXC_SYS_IsClockEnabled(mxc_sys_periph_clock_t clock)
168 {
169     /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 and 64 for the perckcn1 register. */
170     if (clock > 63) {
171         clock -= 64;
172         return !(MXC_LPGCR->pclkdis & (0x1 << clock));
173     } else if (clock > 31) {
174         clock -= 32;
175         return !(MXC_GCR->pclkdis1 & (0x1 << clock));
176     } else {
177         return !(MXC_GCR->pclkdis0 & (0x1 << clock));
178     }
179 }
180 
181 /* ************************************************************************** */
MXC_SYS_ClockDisable(mxc_sys_periph_clock_t clock)182 void MXC_SYS_ClockDisable(mxc_sys_periph_clock_t clock)
183 {
184     /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 and 64 for the perckcn1 register. */
185     if (clock > 63) {
186         clock -= 64;
187         MXC_LPGCR->pclkdis |= (0x1 << clock);
188     } else if (clock > 31) {
189         clock -= 32;
190         MXC_GCR->pclkdis1 |= (0x1 << clock);
191     } else {
192         MXC_GCR->pclkdis0 |= (0x1 << clock);
193     }
194 }
195 
196 /* ************************************************************************** */
MXC_SYS_ClockEnable(mxc_sys_periph_clock_t clock)197 void MXC_SYS_ClockEnable(mxc_sys_periph_clock_t clock)
198 {
199     /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 and 64 for the perckcn1 register. */
200     if (clock > 63) {
201         clock -= 64;
202         MXC_LPGCR->pclkdis &= ~(0x1 << clock);
203     } else if (clock > 31) {
204         clock -= 32;
205         MXC_GCR->pclkdis1 &= ~(0x1 << clock);
206     } else {
207         MXC_GCR->pclkdis0 &= ~(0x1 << clock);
208     }
209 }
210 /* ************************************************************************** */
MXC_SYS_RTCClockEnable()211 void MXC_SYS_RTCClockEnable()
212 {
213     MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_ERTCO_EN;
214 }
215 
216 /* ************************************************************************** */
MXC_SYS_RTCClockDisable(void)217 int MXC_SYS_RTCClockDisable(void)
218 {
219     /* Check that the RTC is not the system clock source */
220     if ((MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_SEL) != MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ERTCO) {
221         MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_ERTCO_EN;
222         return E_NO_ERROR;
223     } else {
224         return E_BAD_STATE;
225     }
226 }
227 
228 /******************************************************************************/
MXC_SYS_ClockSourceEnable(mxc_sys_system_clock_t clock)229 int MXC_SYS_ClockSourceEnable(mxc_sys_system_clock_t clock)
230 {
231     switch (clock) {
232     case MXC_SYS_CLOCK_IPO:
233         MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IPO_EN;
234         return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IPO_RDY);
235         break;
236 
237     case MXC_SYS_CLOCK_IBRO:
238         MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IBRO_EN;
239         return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IBRO_RDY);
240         break;
241 
242     case MXC_SYS_CLOCK_ISO:
243         MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_ISO_EN;
244         return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ISO_RDY);
245         break;
246 
247     case MXC_SYS_CLOCK_EXTCLK:
248         return MXC_GPIO_Config(&gpio_cfg_extclk);
249         break;
250 
251     case MXC_SYS_CLOCK_INRO:
252         // The 80k clock is always enabled
253         return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_INRO_RDY);
254         break;
255 
256     case MXC_SYS_CLOCK_ERFO:
257         MXC_GCR->btleldoctrl |= MXC_F_GCR_BTLELDOCTRL_LDOTXEN | MXC_F_GCR_BTLELDOCTRL_LDORXEN;
258 
259         MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_ERFO_EN;
260         return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ERFO_RDY);
261         break;
262 
263     case MXC_SYS_CLOCK_ERTCO:
264         MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_ERTCO_EN;
265         return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ERTCO_RDY);
266         break;
267 
268     default:
269         return E_BAD_PARAM;
270         break;
271     }
272 }
273 
274 /******************************************************************************/
MXC_SYS_ClockSourceDisable(mxc_sys_system_clock_t clock)275 int MXC_SYS_ClockSourceDisable(mxc_sys_system_clock_t clock)
276 {
277     uint32_t current_clock;
278 
279     current_clock = MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_SEL;
280 
281     // Don't turn off the clock we're running on
282     if (clock == current_clock) {
283         return E_BAD_PARAM;
284     }
285 
286     switch (clock) {
287     case MXC_SYS_CLOCK_IPO:
288         MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_IPO_EN;
289         break;
290 
291     case MXC_SYS_CLOCK_ISO:
292         MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_ISO_EN;
293         break;
294 
295     case MXC_SYS_CLOCK_IBRO:
296         if ((MXC_GCR->pm & MXC_F_GCR_PM_MODE) == MXC_S_GCR_PM_MODE_UPM) {
297             MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_IBRO_EN;
298         }
299         break;
300 
301     case MXC_SYS_CLOCK_EXTCLK:
302         // MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_EXTCLK_EN;
303         break;
304 
305     case MXC_SYS_CLOCK_INRO:
306         // The 80k clock is always enabled
307         break;
308 
309     case MXC_SYS_CLOCK_ERFO:
310         MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_ERFO_EN;
311         break;
312 
313     case MXC_SYS_CLOCK_ERTCO:
314         MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_ERTCO_EN;
315         break;
316 
317     default:
318         return E_BAD_PARAM;
319     }
320 
321     return E_NO_ERROR;
322 }
323 
324 /* ************************************************************************** */
MXC_SYS_Clock_Timeout(uint32_t ready)325 int MXC_SYS_Clock_Timeout(uint32_t ready)
326 {
327     // Start timeout, wait for ready
328     MXC_DelayAsync(MXC_SYS_CLOCK_TIMEOUT, NULL);
329 #ifndef ME18_WLP_TEST
330     /* TODO: Timeout on clock switch, use this for untrimmed parts. */
331     while (!(MXC_GCR->clkctrl & ready)) {}
332     return E_NO_ERROR;
333 #else
334     return E_NO_ERROR;
335 #endif
336 }
337 
338 /* ************************************************************************** */
MXC_SYS_Clock_Select(mxc_sys_system_clock_t clock)339 int MXC_SYS_Clock_Select(mxc_sys_system_clock_t clock)
340 {
341     uint32_t current_clock;
342 
343     // Save the current system clock
344     current_clock = MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_SEL;
345 
346     switch (clock) {
347     case MXC_SYS_CLOCK_IPO:
348 
349         // Enable IPO clock
350         if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_IPO_EN)) {
351             MXC_SYS_ClockSourceEnable(MXC_SYS_CLOCK_IPO);
352         }
353 
354         // Set IPO clock as System Clock
355         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
356                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_IPO);
357 
358         break;
359 
360     case MXC_SYS_CLOCK_ISO:
361 
362         // Enable ISO clock
363         if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_ISO_EN)) {
364             MXC_SYS_ClockSourceEnable(MXC_SYS_CLOCK_ISO);
365         }
366 
367         // Set ISO clock as System Clock
368         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
369                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ISO);
370 
371         break;
372 
373     case MXC_SYS_CLOCK_IBRO:
374 
375         // Enable IBRO clock
376         if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_IBRO_EN)) {
377             MXC_SYS_ClockSourceEnable(MXC_SYS_CLOCK_IBRO);
378         }
379 
380         // Set IBRO clock as System Clock
381         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
382                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_IBRO);
383 
384         break;
385 
386     case MXC_SYS_CLOCK_EXTCLK:
387         MXC_SYS_ClockSourceEnable(MXC_SYS_CLOCK_EXTCLK);
388 
389         // Set external clock clock as System Clock
390         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
391                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_EXTCLK);
392 
393         break;
394 
395     case MXC_SYS_CLOCK_ERFO:
396 
397         // Enable ERFO clock
398         if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_ERFO_EN)) {
399             MXC_SYS_ClockSourceEnable(MXC_SYS_CLOCK_ERFO);
400         }
401 
402         // Set ERFO clock as System Clock
403         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
404                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ERFO);
405 
406         break;
407 
408     case MXC_SYS_CLOCK_INRO:
409         // Set INRO clock as System Clock
410         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
411                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_INRO);
412 
413         break;
414 
415     case MXC_SYS_CLOCK_ERTCO:
416 
417         // Enable ERTCO clock
418         if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_ERTCO_EN)) {
419             MXC_SYS_ClockSourceEnable(MXC_SYS_CLOCK_ERTCO);
420         }
421 
422         // Set ERTCO clock as System Clock
423         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
424                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ERTCO);
425 
426         break;
427 
428     default:
429         return E_BAD_PARAM;
430     }
431 
432     // Wait for system clock to be ready
433     if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_SYSCLK_RDY) != E_NO_ERROR) {
434         // Restore the old system clock if timeout
435         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL, current_clock);
436 
437         return E_TIME_OUT;
438     }
439 
440     // Update the system core clock
441     SystemCoreClockUpdate();
442 
443     return E_NO_ERROR;
444 }
445 
446 /* ************************************************************************** */
MXC_SYS_SetClockDiv(mxc_sys_system_clock_div_t div)447 void MXC_SYS_SetClockDiv(mxc_sys_system_clock_div_t div)
448 {
449     /* Return if this setting is already current */
450     if (div == MXC_SYS_GetClockDiv()) {
451         return;
452     }
453 
454     MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_DIV, div);
455 
456     SystemCoreClockUpdate();
457 }
458 
459 /* ************************************************************************** */
MXC_SYS_GetClockDiv(void)460 mxc_sys_system_clock_div_t MXC_SYS_GetClockDiv(void)
461 {
462     return (MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_DIV);
463 }
464 
465 /* ************************************************************************** */
MXC_SYS_Reset_Periph(mxc_sys_reset_t reset)466 void MXC_SYS_Reset_Periph(mxc_sys_reset_t reset)
467 {
468     /* The mxc_sys_reset_t enum uses enum values that are the offset by 32 and 64 for the rst register. */
469     if (reset > 63) {
470         reset -= 64;
471         MXC_LPGCR->rst = (0x1 << reset);
472         while (MXC_LPGCR->rst & (0x1 << reset)) {}
473     } else if (reset > 31) {
474         reset -= 32;
475         MXC_GCR->rst1 = (0x1 << reset);
476         while (MXC_GCR->rst1 & (0x1 << reset)) {}
477     } else {
478         MXC_GCR->rst0 = (0x1 << reset);
479         while (MXC_GCR->rst0 & (0x1 << reset)) {}
480     }
481 }
482 
483 /* ************************************************************************** */
MXC_SYS_RISCVRun(void)484 void MXC_SYS_RISCVRun(void)
485 {
486     /* Disable the the RSCV */
487     MXC_GCR->pclkdis1 |= MXC_F_GCR_PCLKDIS1_CPU1;
488 
489     /* Set the interrupt vector base address */
490     MXC_FCR->urvbootaddr = (uint32_t)&_binary_riscv_bin_start;
491 
492     /* Power up the RSCV */
493     MXC_GCR->pclkdis1 &= ~(MXC_F_GCR_PCLKDIS1_CPU1);
494 
495     /* CPU1 reset */
496     MXC_GCR->rst1 |= MXC_F_GCR_RST1_CPU1;
497 }
498 
499 /* ************************************************************************** */
MXC_SYS_RISCVShutdown(void)500 void MXC_SYS_RISCVShutdown(void)
501 {
502     /* Disable the the RSCV */
503     MXC_GCR->pclkdis1 |= MXC_F_GCR_PCLKDIS1_CPU1;
504 }
505 
506 /* ************************************************************************** */
MXC_SYS_RiscVClockRate(void)507 uint32_t MXC_SYS_RiscVClockRate(void)
508 {
509     // If in LPM mode and the PCLK is selected as the RV32 clock source,
510     if (((MXC_GCR->pm & MXC_F_GCR_PM_MODE) == MXC_S_GCR_PM_MODE_LPM) &&
511         (MXC_PWRSEQ->lpcn & MXC_F_PWRSEQ_LPCN_ISOCLK_SELECT)) {
512         return ISO_FREQ;
513     } else {
514         return PeripheralClock;
515     }
516 }
517 
518 /* ************************************************************************** */
MXC_SYS_LockDAP_Permanent(void)519 int MXC_SYS_LockDAP_Permanent(void)
520 {
521 #ifdef DEBUG
522     // Locking the DAP is not supported while in DEBUG.
523     // To use this function, build for release ("make release")
524     // or set DEBUG = 0
525     // (see https://analogdevicesinc.github.io/msdk/USERGUIDE/#build-tables)
526     return E_NOT_SUPPORTED;
527 #else
528     int err;
529     uint32_t info_blk_addr;
530     uint32_t lock_sequence[4];
531 
532     // Infoblock address to write lock sequence to
533     info_blk_addr = MXC_INFO_MEM_BASE + INFOBLOCK_DAP_LOCK_OFFSET;
534 
535     // Set lock sequence
536     lock_sequence[0] = DAP_LOCK_SEQUENCE_01;
537     lock_sequence[1] = DAP_LOCK_SEQUENCE_01;
538     lock_sequence[2] = DAP_LOCK_SEQUENCE_23;
539     lock_sequence[3] = DAP_LOCK_SEQUENCE_23;
540 
541     // Initialize FLC
542     MXC_FLC_Init();
543 
544     // Unlock infoblock
545     MXC_FLC_UnlockInfoBlock(info_blk_addr);
546 
547     // Write DAP lock sequence to infoblock
548     err = MXC_FLC_Write128(info_blk_addr, lock_sequence);
549 
550     // Re-lock infoblock
551     MXC_FLC_LockInfoBlock(info_blk_addr);
552 
553     return err;
554 #endif
555 }
556 
557 /**@} end of mxc_sys */
558