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(100)
48
49 // DAP Lock macros
50 #define INFOBLOCK_DAP_LOCK_OFFSET 0x30
51 #define DAP_LOCK_SEQUENCE_01 0x5A5AA5A5
52 #define DAP_LOCK_SEQUENCE_23 0xFFFFFFFF
53
54 /* **** Globals **** */
55
56 /* **** Functions **** */
57
58 /* ************************************************************************** */
MXC_SYS_GetUSN(uint8_t * usn,uint8_t * checksum)59 int MXC_SYS_GetUSN(uint8_t *usn, uint8_t *checksum)
60 {
61 int err = E_NO_ERROR;
62 uint32_t *infoblock = (uint32_t *)MXC_INFO0_MEM_BASE;
63
64 if (usn == NULL) {
65 return E_NULL_PTR;
66 }
67
68 /* Read the USN from the info block */
69 MXC_FLC_UnlockInfoBlock(MXC_INFO0_MEM_BASE);
70
71 memset(usn, 0, MXC_SYS_USN_CHECKSUM_LEN);
72
73 usn[0] = (infoblock[0] & 0x007F8000) >> 15;
74 usn[1] = (infoblock[0] & 0x7F800000) >> 23;
75 usn[2] = (infoblock[1] & 0x0000007F) << 1;
76 usn[2] |= (infoblock[0] & 0x80000000) >> 31;
77 usn[3] = (infoblock[1] & 0x00007F80) >> 7;
78 usn[4] = (infoblock[1] & 0x007F8000) >> 15;
79 usn[5] = (infoblock[1] & 0x7F800000) >> 23;
80 usn[6] = (infoblock[2] & 0x007F8000) >> 15;
81 usn[7] = (infoblock[2] & 0x7F800000) >> 23;
82 usn[8] = (infoblock[3] & 0x0000007F) << 1;
83 usn[8] |= (infoblock[2] & 0x80000000) >> 31;
84 usn[9] = (infoblock[3] & 0x00007F80) >> 7;
85 usn[10] = (infoblock[3] & 0x007F8000) >> 15;
86
87 /* If requested, return the checksum */
88 if (checksum != NULL) {
89 uint8_t check_csum[MXC_SYS_USN_CHECKSUM_LEN];
90 uint8_t aes_key[MXC_SYS_USN_CHECKSUM_LEN] = { 0 }; // NULL Key (per checksum spec)
91
92 // Read Checksum from the infoblock
93 checksum[0] = ((infoblock[3] & 0x7F800000) >> 23);
94 checksum[1] = ((infoblock[4] & 0x007F8000) >> 15);
95
96 err = MXC_AES_Init();
97 if (err) {
98 MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
99 return err;
100 }
101
102 // Set NULL Key
103 MXC_AES_SetExtKey((const void *)aes_key, MXC_AES_128BITS);
104
105 // Compute Checksum
106 mxc_aes_req_t aes_req;
107 aes_req.length = MXC_SYS_USN_CHECKSUM_LEN / 4;
108 aes_req.inputData = (uint32_t *)usn;
109 aes_req.resultData = (uint32_t *)check_csum;
110 aes_req.keySize = MXC_AES_128BITS;
111 aes_req.encryption = MXC_AES_ENCRYPT_EXT_KEY;
112 aes_req.callback = NULL;
113
114 err = MXC_AES_Generic(&aes_req);
115 if (err) {
116 MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
117 return err;
118 }
119
120 MXC_AES_Shutdown();
121
122 // Verify Checksum
123 if (check_csum[0] != checksum[1] || check_csum[1] != checksum[0]) {
124 MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
125 return E_INVALID;
126 }
127 }
128
129 /* Add the info block checksum to the USN */
130 usn[11] = ((infoblock[3] & 0x7F800000) >> 23);
131 usn[12] = ((infoblock[4] & 0x007F8000) >> 15);
132
133 MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
134
135 return err;
136 }
137
138 /* ************************************************************************** */
MXC_SYS_GetRevision(void)139 int MXC_SYS_GetRevision(void)
140 {
141 return (MXC_GCR->revision & MXC_F_GCR_REVISION_REVISION) >> MXC_F_GCR_REVISION_REVISION_POS;
142 }
143
144 /* ************************************************************************** */
MXC_SYS_IsClockEnabled(mxc_sys_periph_clock_t clock)145 int MXC_SYS_IsClockEnabled(mxc_sys_periph_clock_t clock)
146 {
147 /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 and 64 for the perckcn1 register. */
148 if (clock > 63) {
149 clock -= 64;
150 return !(MXC_MCR->pclkdis & (0x1 << clock));
151 } else if (clock > 31) {
152 clock -= 32;
153 return !(MXC_GCR->pclkdis1 & (0x1 << clock));
154 } else {
155 return !(MXC_GCR->pclkdis0 & (0x1 << clock));
156 }
157 }
158
159 /* ************************************************************************** */
MXC_SYS_ClockDisable(mxc_sys_periph_clock_t clock)160 void MXC_SYS_ClockDisable(mxc_sys_periph_clock_t clock)
161 {
162 /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 and 64 for the perckcn1 register. */
163 if (clock > 63) {
164 clock -= 64;
165 MXC_MCR->pclkdis |= (0x1 << clock);
166 } else if (clock > 31) {
167 clock -= 32;
168 MXC_GCR->pclkdis1 |= (0x1 << clock);
169 } else {
170 MXC_GCR->pclkdis0 |= (0x1 << clock);
171 }
172 }
173
174 /* ************************************************************************** */
MXC_SYS_ClockEnable(mxc_sys_periph_clock_t clock)175 void MXC_SYS_ClockEnable(mxc_sys_periph_clock_t clock)
176 {
177 /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 and 64 for the perckcn1 register. */
178 if (clock > 63) {
179 clock -= 64;
180 MXC_MCR->pclkdis &= ~(0x1 << clock);
181 } else if (clock > 31) {
182 clock -= 32;
183 MXC_GCR->pclkdis1 &= ~(0x1 << clock);
184 } else {
185 MXC_GCR->pclkdis0 &= ~(0x1 << clock);
186 }
187 }
188 /* ************************************************************************** */
MXC_SYS_RTCClockEnable()189 void MXC_SYS_RTCClockEnable()
190 {
191 MXC_PWRSEQ->lpcn &= ~(MXC_F_PWRSEQ_LPCN_TM_PWRSEQ);
192 MXC_MCR->clkctrl |= MXC_F_MCR_CLKCTRL_ERTCO_EN;
193 MXC_MCR->clkctrl &= ~(MXC_F_MCR_CLKCTRL_ERTCO_PD);
194 }
195
196 /* ************************************************************************** */
MXC_SYS_RTCClockDisable(void)197 int MXC_SYS_RTCClockDisable(void)
198 {
199 /* Check that the RTC is not the system clock source */
200 if ((MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_SEL) != MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ERTCO) {
201 MXC_MCR->clkctrl &= ~MXC_F_MCR_CLKCTRL_ERTCO_EN;
202 MXC_PWRSEQ->lpcn |= MXC_F_PWRSEQ_LPCN_TM_PWRSEQ;
203 MXC_MCR->clkctrl |= MXC_F_MCR_CLKCTRL_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 // The 80k clock is always enabled
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 is always enabled
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 MXC_DelayAsync(MXC_SYS_CLOCK_TIMEOUT, NULL);
308
309 do {
310 if (MXC_GCR->clkctrl & ready) {
311 MXC_DelayAbort();
312 return E_NO_ERROR;
313 }
314 } while (MXC_DelayCheck() == E_BUSY);
315
316 return E_TIME_OUT;
317 }
318
319 /* ************************************************************************** */
MXC_SYS_Clock_Select(mxc_sys_system_clock_t clock)320 int MXC_SYS_Clock_Select(mxc_sys_system_clock_t clock)
321 {
322 uint32_t current_clock;
323 int err = E_NO_ERROR;
324
325 // Save the current system clock
326 current_clock = MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_SEL;
327
328 switch (clock) {
329 case MXC_SYS_CLOCK_IPO:
330
331 // Enable IPO clock
332 if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_IPO_EN)) {
333 MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IPO_EN;
334
335 // Check if IPO clock is ready
336 if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IPO_RDY) != E_NO_ERROR) {
337 return E_TIME_OUT;
338 }
339 }
340
341 // Set IPO clock as System Clock
342 MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
343 MXC_S_GCR_CLKCTRL_SYSCLK_SEL_IPO);
344
345 break;
346
347 case MXC_SYS_CLOCK_IBRO:
348
349 // Enable IBRO clock
350 if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_IBRO_EN)) {
351 MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IBRO_EN;
352
353 // Check if IBRO clock is ready
354 if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IBRO_RDY) != E_NO_ERROR) {
355 return E_TIME_OUT;
356 }
357 }
358
359 // Set IBRO clock as System Clock
360 MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
361 MXC_S_GCR_CLKCTRL_SYSCLK_SEL_IBRO);
362
363 break;
364
365 case MXC_SYS_CLOCK_EXTCLK:
366 // Enable clock if necessary
367 if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_EXTCLK_RDY) != E_NO_ERROR) {
368 err = MXC_SYS_ClockSourceEnable(MXC_SYS_CLOCK_EXTCLK);
369 if (err)
370 return err;
371 }
372
373 MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
374 MXC_S_GCR_CLKCTRL_SYSCLK_SEL_EXTCLK);
375
376 break;
377
378 case MXC_SYS_CLOCK_ERFO:
379
380 // Enable ERFO clock
381 if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_ERFO_EN)) {
382 MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_ERFO_EN;
383
384 // Check if ERFO clock is ready
385 if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ERFO_RDY) != E_NO_ERROR) {
386 return E_TIME_OUT;
387 }
388 }
389
390 // Set ERFO clock as System Clock
391 MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
392 MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ERFO);
393
394 break;
395
396 case MXC_SYS_CLOCK_INRO:
397 // Set INRO clock as System Clock
398 MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
399 MXC_S_GCR_CLKCTRL_SYSCLK_SEL_INRO);
400
401 break;
402
403 case MXC_SYS_CLOCK_ERTCO:
404
405 // Enable ERTCO clock
406 if (!(MXC_GCR->clkctrl & MXC_F_MCR_CLKCTRL_ERTCO_EN)) {
407 MXC_GCR->clkctrl |= MXC_F_MCR_CLKCTRL_ERTCO_EN;
408
409 // Check if ERTCO clock is ready
410 if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ERTCO_RDY) != E_NO_ERROR) {
411 return E_TIME_OUT;
412 }
413 }
414
415 // Set ERTCO clock as System Clock
416 MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
417 MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ERTCO);
418
419 break;
420
421 default:
422 return E_BAD_PARAM;
423 }
424
425 // Wait for system clock to be ready
426 if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_SYSCLK_RDY) != E_NO_ERROR) {
427 // Restore the old system clock if timeout
428 MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL, current_clock);
429
430 return E_TIME_OUT;
431 }
432
433 // Update the system core clock
434 SystemCoreClockUpdate();
435
436 return E_NO_ERROR;
437 }
438
439 /* ************************************************************************** */
MXC_SYS_SetClockDiv(mxc_sys_system_clock_div_t div)440 void MXC_SYS_SetClockDiv(mxc_sys_system_clock_div_t div)
441 {
442 /* Return if this setting is already current */
443 if (div == MXC_SYS_GetClockDiv()) {
444 return;
445 }
446
447 MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_DIV, div);
448
449 SystemCoreClockUpdate();
450 }
451
452 /* ************************************************************************** */
MXC_SYS_GetClockDiv(void)453 mxc_sys_system_clock_div_t MXC_SYS_GetClockDiv(void)
454 {
455 return (MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_DIV);
456 }
457
458 /* ************************************************************************** */
MXC_SYS_Reset_Periph(mxc_sys_reset_t reset)459 void MXC_SYS_Reset_Periph(mxc_sys_reset_t reset)
460 {
461 /* The mxc_sys_reset_t enum uses enum values that are the offset by 32 and 64 for the rst register. */
462 if (reset > 63) {
463 reset -= 64;
464 MXC_MCR->rst = (0x1 << reset);
465 while (MXC_MCR->rst & (0x1 << reset)) {}
466 } else if (reset > 31) {
467 reset -= 32;
468 MXC_GCR->rst1 = (0x1 << reset);
469 while (MXC_GCR->rst1 & (0x1 << reset)) {}
470 } else {
471 MXC_GCR->rst0 = (0x1 << reset);
472 while (MXC_GCR->rst0 & (0x1 << reset)) {}
473 }
474 }
475
476 /* ************************************************************************** */
MXC_SYS_LockDAP_Permanent(void)477 int MXC_SYS_LockDAP_Permanent(void)
478 {
479 #ifdef DEBUG
480 // Locking the DAP is not supported while in DEBUG.
481 // To use this function, build for release ("make release")
482 // or set DEBUG = 0
483 // (see https://analogdevicesinc.github.io/msdk/USERGUIDE/#build-tables)
484 return E_NOT_SUPPORTED;
485 #else
486 int err;
487 uint32_t info_blk_addr;
488 uint32_t lock_sequence[4];
489
490 // Infoblock address to write lock sequence to
491 info_blk_addr = MXC_INFO_MEM_BASE + INFOBLOCK_DAP_LOCK_OFFSET;
492
493 // Set lock sequence
494 lock_sequence[0] = DAP_LOCK_SEQUENCE_01;
495 lock_sequence[1] = DAP_LOCK_SEQUENCE_01;
496 lock_sequence[2] = DAP_LOCK_SEQUENCE_23;
497 lock_sequence[3] = DAP_LOCK_SEQUENCE_23;
498
499 // Initialize FLC
500 MXC_FLC_Init();
501
502 // Unlock infoblock
503 MXC_FLC_UnlockInfoBlock(info_blk_addr);
504
505 // Write DAP lock sequence to infoblock
506 err = MXC_FLC_Write128(info_blk_addr, lock_sequence);
507
508 // Re-lock infoblock
509 MXC_FLC_LockInfoBlock(info_blk_addr);
510
511 return err;
512 #endif
513 }
514
515 /**@} end of mxc_sys */
516