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