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