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(1)
48 #define MXC_SYS_ERFO_TIMEOUT MSEC(100)
49 
50 // MAX32670 RevB updates may conflict with other parts dependent on RevA version (e.g. MAX32675)
51 #if TARGET_NUM != 32670
52 #define MXC_SYS_RESET_RTC MXC_SYS_RESET0_RTC
53 #endif
54 
55 // DAP Lock macros
56 #define INFOBLOCK_DAP_LOCK_OFFSET 0x30
57 #define DAP_LOCK_SEQUENCE_01 0x5A5AA5A5
58 #define DAP_LOCK_SEQUENCE_23 0xFFFFFFFF
59 
60 /* **** Globals **** */
61 
62 /* **** Functions **** */
63 
64 /* ************************************************************************** */
MXC_SYS_GetUSN(uint8_t * usn,uint8_t * checksum)65 int MXC_SYS_GetUSN(uint8_t *usn, uint8_t *checksum)
66 {
67     uint32_t *infoblock;
68     int err = E_NO_ERROR;
69 
70     if (usn == NULL) {
71         return E_BAD_PARAM;
72     }
73 
74     infoblock = (uint32_t *)MXC_INFO0_MEM_BASE;
75 
76     /* Read the USN from the info block */
77     MXC_FLC_UnlockInfoBlock(MXC_INFO0_MEM_BASE);
78 
79     memset(usn, 0, MXC_SYS_USN_CHECKSUM_LEN);
80 
81     usn[0] = (infoblock[0] & 0x007F8000) >> 15;
82     usn[1] = (infoblock[0] & 0x7F800000) >> 23;
83     usn[2] = (infoblock[1] & 0x0000007F) << 1;
84     usn[2] |= (infoblock[0] & 0x80000000) >> 31;
85     usn[3] = (infoblock[1] & 0x00007F80) >> 7;
86     usn[4] = (infoblock[1] & 0x007F8000) >> 15;
87     usn[5] = (infoblock[1] & 0x7F800000) >> 23;
88     usn[6] = (infoblock[2] & 0x007F8000) >> 15;
89     usn[7] = (infoblock[2] & 0x7F800000) >> 23;
90     usn[8] = (infoblock[3] & 0x0000007F) << 1;
91     usn[8] |= (infoblock[2] & 0x80000000) >> 31;
92     usn[9] = (infoblock[3] & 0x00007F80) >> 7;
93     usn[10] = (infoblock[3] & 0x007F8000) >> 15;
94 
95     /* If requested, verify and return the checksum */
96     if (checksum != NULL) {
97         uint8_t check_csum[MXC_SYS_USN_CHECKSUM_LEN];
98         uint8_t aes_key[MXC_SYS_USN_CHECKSUM_LEN] = { 0 }; // NULL Key (per checksum spec)
99 
100         // Read Checksum from the infoblock
101         checksum[0] = ((infoblock[3] & 0x7F800000) >> 23);
102         checksum[1] = ((infoblock[4] & 0x007F8000) >> 15);
103 
104         err = MXC_AES_Init();
105         if (err) {
106             MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
107             return err;
108         }
109 
110         // Set NULL Key
111         MXC_AES_SetExtKey((const void *)aes_key, MXC_AES_128BITS);
112 
113         // Compute Checksum
114         mxc_aes_req_t aes_req;
115         aes_req.length = MXC_SYS_USN_CHECKSUM_LEN / 4;
116         aes_req.inputData = (uint32_t *)usn;
117         aes_req.resultData = (uint32_t *)check_csum;
118         aes_req.keySize = MXC_AES_128BITS;
119         aes_req.encryption = MXC_AES_ENCRYPT_EXT_KEY;
120         aes_req.callback = NULL;
121 
122         err = MXC_AES_Generic(&aes_req);
123         if (err) {
124             MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
125             return err;
126         }
127 
128         MXC_AES_Shutdown();
129 
130         // Verify Checksum
131         if (check_csum[0] != checksum[1] || check_csum[1] != checksum[0]) {
132             MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
133             return E_INVALID;
134         }
135     }
136 
137     /* Add the info block checksum to the USN */
138     usn[11] = ((infoblock[3] & 0x7F800000) >> 23);
139     usn[12] = ((infoblock[4] & 0x007F8000) >> 15);
140 
141     MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
142 
143     return E_NO_ERROR;
144 }
145 
146 /* ************************************************************************** */
MXC_SYS_IsClockEnabled(mxc_sys_periph_clock_t clock)147 int MXC_SYS_IsClockEnabled(mxc_sys_periph_clock_t clock)
148 {
149     /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 and 64 for the perckcn1 register. */
150     if (clock > 63) {
151         clock -= 64;
152         return !(MXC_MCR->clkdis & (0x1 << clock));
153     } else if (clock > 31) {
154         clock -= 32;
155         return !(MXC_GCR->pclkdis1 & (0x1 << clock));
156     } else {
157         return !(MXC_GCR->pclkdis0 & (0x1 << clock));
158     }
159 }
160 
161 /* ************************************************************************** */
MXC_SYS_ClockDisable(mxc_sys_periph_clock_t clock)162 void MXC_SYS_ClockDisable(mxc_sys_periph_clock_t clock)
163 {
164     /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 and 64 for the perckcn1 register. */
165     if (clock > 63) {
166         clock -= 64;
167         MXC_MCR->clkdis |= (0x1 << clock);
168     } else if (clock > 31) {
169         clock -= 32;
170         MXC_GCR->pclkdis1 |= (0x1 << clock);
171     } else {
172         MXC_GCR->pclkdis0 |= (0x1 << clock);
173     }
174 }
175 
176 /* ************************************************************************** */
MXC_SYS_ClockEnable(mxc_sys_periph_clock_t clock)177 void MXC_SYS_ClockEnable(mxc_sys_periph_clock_t clock)
178 {
179     /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 and 64 for the perckcn1 register. */
180     if (clock > 63) {
181         clock -= 64;
182         MXC_MCR->clkdis &= ~(0x1 << clock);
183     } else if (clock > 31) {
184         clock -= 32;
185         MXC_GCR->pclkdis1 &= ~(0x1 << clock);
186     } else {
187         MXC_GCR->pclkdis0 &= ~(0x1 << clock);
188     }
189 }
190 /* ************************************************************************** */
MXC_SYS_RTCClockEnable()191 void MXC_SYS_RTCClockEnable()
192 {
193     MXC_PWRSEQ->lpcn &= ~(MXC_F_PWRSEQ_LPCN_ERTCO_PD); // For Rev B parts
194     MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_ERTCO_EN; // For Rev A parts
195 }
196 
197 /* ************************************************************************** */
MXC_SYS_RTCClockDisable(void)198 int MXC_SYS_RTCClockDisable(void)
199 {
200     /* Check that the RTC is not the system clock source */
201     if ((MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_SEL) != MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ERTCO) {
202         MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_ERTCO_EN;
203         MXC_PWRSEQ->lpcn |= MXC_F_PWRSEQ_LPCN_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         MXC_PWRSEQ->lpcn |= MXC_F_PWRSEQ_LPCN_INRO_EN;
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 can't be disabled through software.
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     if (ready == MXC_F_GCR_CLKCTRL_ERFO_RDY) {
308         MXC_DelayAsync(MXC_SYS_ERFO_TIMEOUT, NULL);
309     } else {
310         MXC_DelayAsync(MXC_SYS_CLOCK_TIMEOUT, NULL);
311     }
312 
313     do {
314         if (MXC_GCR->clkctrl & ready) {
315             MXC_DelayAbort();
316             return E_NO_ERROR;
317         }
318     } while (MXC_DelayCheck() == E_BUSY);
319 
320     return E_TIME_OUT;
321 }
322 
323 /* ************************************************************************** */
MXC_SYS_Clock_Select(mxc_sys_system_clock_t clock)324 int MXC_SYS_Clock_Select(mxc_sys_system_clock_t clock)
325 {
326     uint32_t current_clock;
327     int err = E_NO_ERROR;
328 
329     // Save the current system clock
330     current_clock = MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_SEL;
331 
332     switch (clock) {
333     case MXC_SYS_CLOCK_IPO:
334 
335         // Enable IPO clock
336         if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_IPO_EN)) {
337             MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IPO_EN;
338 
339             // Check if IPO clock is ready
340             if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IPO_RDY) != E_NO_ERROR) {
341                 return E_TIME_OUT;
342             }
343         }
344 
345         // Set IPO clock as System Clock
346         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
347                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_IPO);
348 
349         break;
350 
351     case MXC_SYS_CLOCK_IBRO:
352 
353         // Enable IBRO clock
354         if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_IBRO_EN)) {
355             MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IBRO_EN;
356 
357             // Check if IBRO clock is ready
358             if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IBRO_RDY) != E_NO_ERROR) {
359                 return E_TIME_OUT;
360             }
361         }
362 
363         // Set IBRO clock as System Clock
364         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
365                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_IBRO);
366 
367         break;
368 
369     case MXC_SYS_CLOCK_EXTCLK:
370         // Enable clock if necessary
371         if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_EXTCLK_RDY) != E_NO_ERROR) {
372             err = MXC_SYS_ClockSourceEnable(MXC_SYS_CLOCK_EXTCLK);
373             if (err)
374                 return err;
375         }
376 
377         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
378                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_EXTCLK);
379 
380         break;
381 
382     case MXC_SYS_CLOCK_ERFO:
383 
384         // Enable ERFO clock
385         if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_ERFO_EN)) {
386             MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_ERFO_EN;
387 
388             // Check if ERFO clock is ready
389             if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ERFO_RDY) != E_NO_ERROR) {
390                 return E_TIME_OUT;
391             }
392         }
393 
394         // Set ERFO clock as System Clock
395         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
396                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ERFO);
397 
398         break;
399 
400     case MXC_SYS_CLOCK_INRO:
401         // Set INRO clock as System Clock
402         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
403                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_INRO);
404 
405         break;
406 
407     case MXC_SYS_CLOCK_ERTCO:
408 
409         // Enable ERTCO clock
410         if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_ERTCO_EN)) {
411             MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_ERTCO_EN;
412 
413             // Check if ERTCO clock is ready
414             if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ERTCO_RDY) != E_NO_ERROR) {
415                 return E_TIME_OUT;
416             }
417         }
418 
419         // Set ERTCO clock as System Clock
420         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
421                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ERTCO);
422 
423         break;
424 
425     default:
426         return E_BAD_PARAM;
427     }
428 
429     // Wait for system clock to be ready
430     if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_SYSCLK_RDY) != E_NO_ERROR) {
431         // Restore the old system clock if timeout
432         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL, current_clock);
433 
434         return E_TIME_OUT;
435     }
436 
437     // Update the system core clock
438     SystemCoreClockUpdate();
439 
440     return E_NO_ERROR;
441 }
442 
443 /* ************************************************************************** */
MXC_SYS_SetClockDiv(mxc_sys_system_clock_div_t div)444 void MXC_SYS_SetClockDiv(mxc_sys_system_clock_div_t div)
445 {
446     /* Return if this setting is already current */
447     if (div == MXC_SYS_GetClockDiv()) {
448         return;
449     }
450 
451     MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_DIV, div);
452 
453     SystemCoreClockUpdate();
454 }
455 
456 /* ************************************************************************** */
MXC_SYS_GetClockDiv(void)457 mxc_sys_system_clock_div_t MXC_SYS_GetClockDiv(void)
458 {
459     return (MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_DIV);
460 }
461 
462 /* ************************************************************************** */
MXC_SYS_Reset_Periph(mxc_sys_reset_t reset)463 void MXC_SYS_Reset_Periph(mxc_sys_reset_t reset)
464 {
465     // RTC reset bit is different for RevA and RevB
466     if (reset == MXC_SYS_RESET_RTC) {
467         // If RevA, switch to reset bit in RST0
468         if ((MXC_GCR->revision & 0x00F0) == 0xA0) {
469             reset = MXC_F_GCR_RST0_RTC_POS;
470         }
471     }
472 
473     /* The mxc_sys_reset_t enum uses enum values that are the offset by 32 and 64 for the rst register. */
474     if (reset > 63) {
475         reset -= 64;
476         MXC_MCR->rst = (0x1 << reset);
477         while (MXC_MCR->rst & (0x1 << reset)) {}
478     } else if (reset > 31) {
479         reset -= 32;
480         MXC_GCR->rst1 = (0x1 << reset);
481         while (MXC_GCR->rst1 & (0x1 << reset)) {}
482     } else {
483         MXC_GCR->rst0 = (0x1 << reset);
484         while (MXC_GCR->rst0 & (0x1 << reset)) {}
485     }
486 }
487 
488 /* ************************************************************************** */
MXC_SYS_LockDAP_Permanent(void)489 int MXC_SYS_LockDAP_Permanent(void)
490 {
491 #ifdef DEBUG
492     // Locking the DAP is not supported while in DEBUG.
493     // To use this function, build for release ("make release")
494     // or set DEBUG = 0
495     // (see https://analogdevicesinc.github.io/msdk/USERGUIDE/#build-tables)
496     return E_NOT_SUPPORTED;
497 #else
498     int err;
499     uint32_t info_blk_addr;
500     uint32_t lock_sequence[4];
501 
502     // Infoblock address to write lock sequence to
503     info_blk_addr = MXC_INFO_MEM_BASE + INFOBLOCK_DAP_LOCK_OFFSET;
504 
505     // Set lock sequence
506     lock_sequence[0] = DAP_LOCK_SEQUENCE_01;
507     lock_sequence[1] = DAP_LOCK_SEQUENCE_01;
508     lock_sequence[2] = DAP_LOCK_SEQUENCE_23;
509     lock_sequence[3] = DAP_LOCK_SEQUENCE_23;
510 
511     // Initialize FLC
512     MXC_FLC_Init();
513 
514     // Unlock infoblock
515     MXC_FLC_UnlockInfoBlock(info_blk_addr);
516 
517     // Write DAP lock sequence to infoblock
518     err = MXC_FLC_Write128(info_blk_addr, lock_sequence);
519 
520     // Re-lock infoblock
521     MXC_FLC_LockInfoBlock(info_blk_addr);
522 
523     return err;
524 #endif
525 }
526 
527 /**@} end of mxc_sys */
528