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