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