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