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 "aes.h"
35 #include "flc.h"
36 #include "gcr_regs.h"
37 #include "fcr_regs.h"
38 #include "mcr_regs.h"
39 #include "pwrseq_regs.h"
40 
41 /**
42  * @ingroup mxc_sys
43  * @{
44  */
45 
46 /* **** Definitions **** */
47 #define MXC_SYS_CLOCK_TIMEOUT MSEC(100)
48 
49 // DAP Lock macros
50 #define INFOBLOCK_DAP_LOCK_OFFSET 0x30
51 #define DAP_LOCK_SEQUENCE_01 0x5A5AA5A5
52 #define DAP_LOCK_SEQUENCE_23 0xFFFFFFFF
53 
54 /* **** Globals **** */
55 
56 /* **** Functions **** */
57 
58 /* ************************************************************************** */
MXC_SYS_GetUSN(uint8_t * usn,uint8_t * checksum)59 int MXC_SYS_GetUSN(uint8_t *usn, uint8_t *checksum)
60 {
61     int err = E_NO_ERROR;
62     uint32_t *infoblock = (uint32_t *)MXC_INFO0_MEM_BASE;
63 
64     if (usn == NULL) {
65         return E_NULL_PTR;
66     }
67 
68     /* Read the USN from the info block */
69     MXC_FLC_UnlockInfoBlock(MXC_INFO0_MEM_BASE);
70 
71     memset(usn, 0, MXC_SYS_USN_CHECKSUM_LEN);
72 
73     usn[0] = (infoblock[0] & 0x007F8000) >> 15;
74     usn[1] = (infoblock[0] & 0x7F800000) >> 23;
75     usn[2] = (infoblock[1] & 0x0000007F) << 1;
76     usn[2] |= (infoblock[0] & 0x80000000) >> 31;
77     usn[3] = (infoblock[1] & 0x00007F80) >> 7;
78     usn[4] = (infoblock[1] & 0x007F8000) >> 15;
79     usn[5] = (infoblock[1] & 0x7F800000) >> 23;
80     usn[6] = (infoblock[2] & 0x007F8000) >> 15;
81     usn[7] = (infoblock[2] & 0x7F800000) >> 23;
82     usn[8] = (infoblock[3] & 0x0000007F) << 1;
83     usn[8] |= (infoblock[2] & 0x80000000) >> 31;
84     usn[9] = (infoblock[3] & 0x00007F80) >> 7;
85     usn[10] = (infoblock[3] & 0x007F8000) >> 15;
86 
87     /* If requested, return the checksum */
88     if (checksum != NULL) {
89         uint8_t check_csum[MXC_SYS_USN_CHECKSUM_LEN];
90         uint8_t aes_key[MXC_SYS_USN_CHECKSUM_LEN] = { 0 }; // NULL Key (per checksum spec)
91 
92         // Read Checksum from the infoblock
93         checksum[0] = ((infoblock[3] & 0x7F800000) >> 23);
94         checksum[1] = ((infoblock[4] & 0x007F8000) >> 15);
95 
96         err = MXC_AES_Init();
97         if (err) {
98             MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
99             return err;
100         }
101 
102         // Set NULL Key
103         MXC_AES_SetExtKey((const void *)aes_key, MXC_AES_128BITS);
104 
105         // Compute Checksum
106         mxc_aes_req_t aes_req;
107         aes_req.length = MXC_SYS_USN_CHECKSUM_LEN / 4;
108         aes_req.inputData = (uint32_t *)usn;
109         aes_req.resultData = (uint32_t *)check_csum;
110         aes_req.keySize = MXC_AES_128BITS;
111         aes_req.encryption = MXC_AES_ENCRYPT_EXT_KEY;
112         aes_req.callback = NULL;
113 
114         err = MXC_AES_Generic(&aes_req);
115         if (err) {
116             MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
117             return err;
118         }
119 
120         MXC_AES_Shutdown();
121 
122         // Verify Checksum
123         if (check_csum[0] != checksum[1] || check_csum[1] != checksum[0]) {
124             MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
125             return E_INVALID;
126         }
127     }
128 
129     /* Add the info block checksum to the USN */
130     usn[11] = ((infoblock[3] & 0x7F800000) >> 23);
131     usn[12] = ((infoblock[4] & 0x007F8000) >> 15);
132 
133     MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
134 
135     return err;
136 }
137 
138 /* ************************************************************************** */
MXC_SYS_GetRevision(void)139 int MXC_SYS_GetRevision(void)
140 {
141     return (MXC_GCR->revision & MXC_F_GCR_REVISION_REVISION) >> MXC_F_GCR_REVISION_REVISION_POS;
142 }
143 
144 /* ************************************************************************** */
MXC_SYS_IsClockEnabled(mxc_sys_periph_clock_t clock)145 int MXC_SYS_IsClockEnabled(mxc_sys_periph_clock_t clock)
146 {
147     /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 and 64 for the perckcn1 register. */
148     if (clock > 63) {
149         clock -= 64;
150         return !(MXC_MCR->pclkdis & (0x1 << clock));
151     } else if (clock > 31) {
152         clock -= 32;
153         return !(MXC_GCR->pclkdis1 & (0x1 << clock));
154     } else {
155         return !(MXC_GCR->pclkdis0 & (0x1 << clock));
156     }
157 }
158 
159 /* ************************************************************************** */
MXC_SYS_ClockDisable(mxc_sys_periph_clock_t clock)160 void MXC_SYS_ClockDisable(mxc_sys_periph_clock_t clock)
161 {
162     /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 and 64 for the perckcn1 register. */
163     if (clock > 63) {
164         clock -= 64;
165         MXC_MCR->pclkdis |= (0x1 << clock);
166     } else if (clock > 31) {
167         clock -= 32;
168         MXC_GCR->pclkdis1 |= (0x1 << clock);
169     } else {
170         MXC_GCR->pclkdis0 |= (0x1 << clock);
171     }
172 }
173 
174 /* ************************************************************************** */
MXC_SYS_ClockEnable(mxc_sys_periph_clock_t clock)175 void MXC_SYS_ClockEnable(mxc_sys_periph_clock_t clock)
176 {
177     /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 and 64 for the perckcn1 register. */
178     if (clock > 63) {
179         clock -= 64;
180         MXC_MCR->pclkdis &= ~(0x1 << clock);
181     } else if (clock > 31) {
182         clock -= 32;
183         MXC_GCR->pclkdis1 &= ~(0x1 << clock);
184     } else {
185         MXC_GCR->pclkdis0 &= ~(0x1 << clock);
186     }
187 }
188 /* ************************************************************************** */
MXC_SYS_RTCClockEnable()189 void MXC_SYS_RTCClockEnable()
190 {
191     MXC_PWRSEQ->lpcn &= ~(MXC_F_PWRSEQ_LPCN_TM_PWRSEQ);
192     MXC_MCR->clkctrl |= MXC_F_MCR_CLKCTRL_ERTCO_EN;
193     MXC_MCR->clkctrl &= ~(MXC_F_MCR_CLKCTRL_ERTCO_PD);
194 }
195 
196 /* ************************************************************************** */
MXC_SYS_RTCClockDisable(void)197 int MXC_SYS_RTCClockDisable(void)
198 {
199     /* Check that the RTC is not the system clock source */
200     if ((MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_SEL) != MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ERTCO) {
201         MXC_MCR->clkctrl &= ~MXC_F_MCR_CLKCTRL_ERTCO_EN;
202         MXC_PWRSEQ->lpcn |= MXC_F_PWRSEQ_LPCN_TM_PWRSEQ;
203         MXC_MCR->clkctrl |= MXC_F_MCR_CLKCTRL_ERTCO_PD;
204         return E_NO_ERROR;
205     } else {
206         return E_BAD_STATE;
207     }
208 }
209 
210 /******************************************************************************/
MXC_SYS_ClockSourceEnable(mxc_sys_system_clock_t clock)211 int MXC_SYS_ClockSourceEnable(mxc_sys_system_clock_t clock)
212 {
213     int err = E_NO_ERROR;
214 
215     switch (clock) {
216     case MXC_SYS_CLOCK_IPO:
217         MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IPO_EN;
218         return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IPO_RDY);
219         break;
220 
221     case MXC_SYS_CLOCK_IBRO:
222         MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IBRO_EN;
223         return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IBRO_RDY);
224         break;
225 
226     case MXC_SYS_CLOCK_EXTCLK:
227         err = MXC_GPIO_Config(&gpio_cfg_hfextclk);
228         if (err)
229             return err;
230         return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_EXTCLK_RDY);
231         break;
232 
233     case MXC_SYS_CLOCK_INRO:
234         // The 80k clock is always enabled
235         return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_INRO_RDY);
236         break;
237 
238     case MXC_SYS_CLOCK_ERFO:
239         MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_ERFO_EN;
240         return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ERFO_RDY);
241         break;
242 
243     case MXC_SYS_CLOCK_ERTCO:
244         MXC_SYS_RTCClockEnable();
245         return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ERTCO_RDY);
246         break;
247 
248     default:
249         return E_BAD_PARAM;
250         break;
251     }
252 }
253 
254 /******************************************************************************/
MXC_SYS_ClockSourceDisable(mxc_sys_system_clock_t clock)255 int MXC_SYS_ClockSourceDisable(mxc_sys_system_clock_t clock)
256 {
257     uint32_t current_clock;
258 
259     current_clock = MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_SEL;
260 
261     // Don't turn off the clock we're running on
262     if (clock == current_clock) {
263         return E_BAD_PARAM;
264     }
265 
266     switch (clock) {
267     case MXC_SYS_CLOCK_IPO:
268         MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_IPO_EN;
269         break;
270 
271     case MXC_SYS_CLOCK_IBRO:
272         MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_IBRO_EN;
273         break;
274 
275     case MXC_SYS_CLOCK_EXTCLK:
276         /*
277         There's not a great way to disable the external clock.
278         Deinitializing the GPIO here may have unintended consequences
279         for application code.
280         Selecting a different system clock source is sufficient
281         to "disable" the EXT_CLK source.
282         */
283         break;
284 
285     case MXC_SYS_CLOCK_INRO:
286         // The 80k clock is always enabled
287         break;
288 
289     case MXC_SYS_CLOCK_ERFO:
290         MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_ERFO_EN;
291         break;
292 
293     case MXC_SYS_CLOCK_ERTCO:
294         return MXC_SYS_RTCClockDisable();
295 
296     default:
297         return E_BAD_PARAM;
298     }
299 
300     return E_NO_ERROR;
301 }
302 
303 /* ************************************************************************** */
MXC_SYS_Clock_Timeout(uint32_t ready)304 int MXC_SYS_Clock_Timeout(uint32_t ready)
305 {
306     // Start timeout, wait for ready
307     MXC_DelayAsync(MXC_SYS_CLOCK_TIMEOUT, NULL);
308 
309     do {
310         if (MXC_GCR->clkctrl & ready) {
311             MXC_DelayAbort();
312             return E_NO_ERROR;
313         }
314     } while (MXC_DelayCheck() == E_BUSY);
315 
316     return E_TIME_OUT;
317 }
318 
319 /* ************************************************************************** */
MXC_SYS_Clock_Select(mxc_sys_system_clock_t clock)320 int MXC_SYS_Clock_Select(mxc_sys_system_clock_t clock)
321 {
322     uint32_t current_clock;
323     int err = E_NO_ERROR;
324 
325     // Save the current system clock
326     current_clock = MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_SEL;
327 
328     switch (clock) {
329     case MXC_SYS_CLOCK_IPO:
330 
331         // Enable IPO clock
332         if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_IPO_EN)) {
333             MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IPO_EN;
334 
335             // Check if IPO clock is ready
336             if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IPO_RDY) != E_NO_ERROR) {
337                 return E_TIME_OUT;
338             }
339         }
340 
341         // Set IPO clock as System Clock
342         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
343                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_IPO);
344 
345         break;
346 
347     case MXC_SYS_CLOCK_IBRO:
348 
349         // Enable IBRO clock
350         if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_IBRO_EN)) {
351             MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IBRO_EN;
352 
353             // Check if IBRO clock is ready
354             if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IBRO_RDY) != E_NO_ERROR) {
355                 return E_TIME_OUT;
356             }
357         }
358 
359         // Set IBRO clock as System Clock
360         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
361                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_IBRO);
362 
363         break;
364 
365     case MXC_SYS_CLOCK_EXTCLK:
366         // Enable clock if necessary
367         if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_EXTCLK_RDY) != E_NO_ERROR) {
368             err = MXC_SYS_ClockSourceEnable(MXC_SYS_CLOCK_EXTCLK);
369             if (err)
370                 return err;
371         }
372 
373         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
374                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_EXTCLK);
375 
376         break;
377 
378     case MXC_SYS_CLOCK_ERFO:
379 
380         // Enable ERFO clock
381         if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_ERFO_EN)) {
382             MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_ERFO_EN;
383 
384             // Check if ERFO clock is ready
385             if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ERFO_RDY) != E_NO_ERROR) {
386                 return E_TIME_OUT;
387             }
388         }
389 
390         // Set ERFO clock as System Clock
391         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
392                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ERFO);
393 
394         break;
395 
396     case MXC_SYS_CLOCK_INRO:
397         // Set INRO clock as System Clock
398         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
399                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_INRO);
400 
401         break;
402 
403     case MXC_SYS_CLOCK_ERTCO:
404 
405         // Enable ERTCO clock
406         if (!(MXC_GCR->clkctrl & MXC_F_MCR_CLKCTRL_ERTCO_EN)) {
407             MXC_GCR->clkctrl |= MXC_F_MCR_CLKCTRL_ERTCO_EN;
408 
409             // Check if ERTCO clock is ready
410             if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ERTCO_RDY) != E_NO_ERROR) {
411                 return E_TIME_OUT;
412             }
413         }
414 
415         // Set ERTCO clock as System Clock
416         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
417                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ERTCO);
418 
419         break;
420 
421     default:
422         return E_BAD_PARAM;
423     }
424 
425     // Wait for system clock to be ready
426     if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_SYSCLK_RDY) != E_NO_ERROR) {
427         // Restore the old system clock if timeout
428         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL, current_clock);
429 
430         return E_TIME_OUT;
431     }
432 
433     // Update the system core clock
434     SystemCoreClockUpdate();
435 
436     return E_NO_ERROR;
437 }
438 
439 /* ************************************************************************** */
MXC_SYS_SetClockDiv(mxc_sys_system_clock_div_t div)440 void MXC_SYS_SetClockDiv(mxc_sys_system_clock_div_t div)
441 {
442     /* Return if this setting is already current */
443     if (div == MXC_SYS_GetClockDiv()) {
444         return;
445     }
446 
447     MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_DIV, div);
448 
449     SystemCoreClockUpdate();
450 }
451 
452 /* ************************************************************************** */
MXC_SYS_GetClockDiv(void)453 mxc_sys_system_clock_div_t MXC_SYS_GetClockDiv(void)
454 {
455     return (MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_DIV);
456 }
457 
458 /* ************************************************************************** */
MXC_SYS_Reset_Periph(mxc_sys_reset_t reset)459 void MXC_SYS_Reset_Periph(mxc_sys_reset_t reset)
460 {
461     /* The mxc_sys_reset_t enum uses enum values that are the offset by 32 and 64 for the rst register. */
462     if (reset > 63) {
463         reset -= 64;
464         MXC_MCR->rst = (0x1 << reset);
465         while (MXC_MCR->rst & (0x1 << reset)) {}
466     } else if (reset > 31) {
467         reset -= 32;
468         MXC_GCR->rst1 = (0x1 << reset);
469         while (MXC_GCR->rst1 & (0x1 << reset)) {}
470     } else {
471         MXC_GCR->rst0 = (0x1 << reset);
472         while (MXC_GCR->rst0 & (0x1 << reset)) {}
473     }
474 }
475 
476 /* ************************************************************************** */
MXC_SYS_LockDAP_Permanent(void)477 int MXC_SYS_LockDAP_Permanent(void)
478 {
479 #ifdef DEBUG
480     // Locking the DAP is not supported while in DEBUG.
481     // To use this function, build for release ("make release")
482     // or set DEBUG = 0
483     // (see https://analogdevicesinc.github.io/msdk/USERGUIDE/#build-tables)
484     return E_NOT_SUPPORTED;
485 #else
486     int err;
487     uint32_t info_blk_addr;
488     uint32_t lock_sequence[4];
489 
490     // Infoblock address to write lock sequence to
491     info_blk_addr = MXC_INFO_MEM_BASE + INFOBLOCK_DAP_LOCK_OFFSET;
492 
493     // Set lock sequence
494     lock_sequence[0] = DAP_LOCK_SEQUENCE_01;
495     lock_sequence[1] = DAP_LOCK_SEQUENCE_01;
496     lock_sequence[2] = DAP_LOCK_SEQUENCE_23;
497     lock_sequence[3] = DAP_LOCK_SEQUENCE_23;
498 
499     // Initialize FLC
500     MXC_FLC_Init();
501 
502     // Unlock infoblock
503     MXC_FLC_UnlockInfoBlock(info_blk_addr);
504 
505     // Write DAP lock sequence to infoblock
506     err = MXC_FLC_Write128(info_blk_addr, lock_sequence);
507 
508     // Re-lock infoblock
509     MXC_FLC_LockInfoBlock(info_blk_addr);
510 
511     return err;
512 #endif
513 }
514 
515 /**@} end of mxc_sys */
516