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 "aes.h"
31 #include "flc.h"
32 #include "fcr_regs.h"
33 #include "gcr_regs.h"
34 #include "mcr_regs.h"
35 #include "mxc_assert.h"
36 #include "mxc_delay.h"
37 #include "mxc_device.h"
38 #include "mxc_sys.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
49 // DAP Lock macros
50 #define INFOBLOCK_DAP_LOCK_OFFSET 0x30
51 #define DAP_LOCK_SEQUENCE 0x5A5AA5A5
52
53 /* **** Globals **** */
54
55 /* Symbol defined when loading RISCV image */
56 extern uint32_t _binary_riscv_bin_start;
57
58 /* **** Functions **** */
59
60 /* ************************************************************************** */
MXC_SYS_GetUSN(uint8_t * usn,uint8_t * checksum)61 int MXC_SYS_GetUSN(uint8_t *usn, uint8_t *checksum)
62 {
63 int err = E_NO_ERROR;
64 uint32_t *infoblock = (uint32_t *)MXC_INFO0_MEM_BASE;
65
66 if (usn == NULL) {
67 return E_NULL_PTR;
68 }
69
70 /* Read the USN from the info block */
71 MXC_FLC_UnlockInfoBlock(MXC_INFO0_MEM_BASE);
72
73 memset(usn, 0, MXC_SYS_USN_CHECKSUM_LEN);
74
75 usn[0] = (infoblock[0] & 0x007F8000) >> 15;
76 usn[1] = (infoblock[0] & 0x7F800000) >> 23;
77 usn[2] = (infoblock[1] & 0x0000007F) << 1;
78 usn[2] |= (infoblock[0] & 0x80000000) >> 31;
79 usn[3] = (infoblock[1] & 0x00007F80) >> 7;
80 usn[4] = (infoblock[1] & 0x007F8000) >> 15;
81 usn[5] = (infoblock[1] & 0x7F800000) >> 23;
82 usn[6] = (infoblock[2] & 0x007F8000) >> 15;
83 usn[7] = (infoblock[2] & 0x7F800000) >> 23;
84 usn[8] = (infoblock[3] & 0x0000007F) << 1;
85 usn[8] |= (infoblock[2] & 0x80000000) >> 31;
86 usn[9] = (infoblock[3] & 0x00007F80) >> 7;
87 usn[10] = (infoblock[3] & 0x007F8000) >> 15;
88
89 if (checksum != NULL) {
90 uint8_t check_csum[MXC_SYS_USN_CHECKSUM_LEN];
91 uint8_t aes_key[MXC_SYS_USN_CHECKSUM_LEN] = { 0 }; // NULL Key (per checksum spec)
92
93 // Read Checksum from the infoblock
94 checksum[0] = ((infoblock[3] & 0x7F800000) >> 23);
95 checksum[1] = ((infoblock[4] & 0x007F8000) >> 15);
96
97 err = MXC_AES_Init();
98 if (err) {
99 MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
100 return err;
101 }
102
103 // Set NULL Key
104 MXC_AES_SetExtKey((const void *)aes_key, MXC_AES_128BITS);
105
106 // Compute Checksum
107 mxc_aes_req_t aes_req;
108 aes_req.length = MXC_SYS_USN_CHECKSUM_LEN / 4;
109 aes_req.inputData = (uint32_t *)usn;
110 aes_req.resultData = (uint32_t *)check_csum;
111 aes_req.keySize = MXC_AES_128BITS;
112 aes_req.encryption = MXC_AES_ENCRYPT_EXT_KEY;
113 aes_req.callback = NULL;
114
115 err = MXC_AES_Generic(&aes_req);
116 if (err) {
117 MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
118 return err;
119 }
120
121 MXC_AES_Shutdown();
122
123 // Verify Checksum
124 if (check_csum[0] != checksum[1] || check_csum[1] != checksum[0]) {
125 MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
126 return E_INVALID;
127 }
128 }
129
130 /* Add the info block checksum to the USN */
131 usn[11] = ((infoblock[3] & 0x7F800000) >> 23);
132 usn[12] = ((infoblock[4] & 0x007F8000) >> 15);
133
134 MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
135
136 return err;
137 }
138
139 /* ************************************************************************** */
MXC_SYS_IsClockEnabled(mxc_sys_periph_clock_t clock)140 int MXC_SYS_IsClockEnabled(mxc_sys_periph_clock_t clock)
141 {
142 if (clock > 31) {
143 clock -= 32;
144 return !(MXC_GCR->pclkdis1 & (0x1 << clock));
145 } else if (clock > 63) {
146 clock -= 64;
147 return !(MXC_MCR->pclkdis & (0x1 << clock));
148 } else {
149 return !(MXC_GCR->pclkdis0 & (0x1 << clock));
150 }
151 }
152
153 /* ************************************************************************** */
MXC_SYS_ClockDisable(mxc_sys_periph_clock_t clock)154 void MXC_SYS_ClockDisable(mxc_sys_periph_clock_t clock)
155 {
156 if (clock > 63) {
157 clock -= 64;
158 MXC_MCR->pclkdis |= (0x1 << clock);
159 } else if (clock > 31) {
160 clock -= 32;
161 MXC_GCR->pclkdis1 |= (0x1 << clock);
162 } else {
163 MXC_GCR->pclkdis0 |= (0x1 << clock);
164 }
165 }
166
167 /* ************************************************************************** */
MXC_SYS_ClockEnable(mxc_sys_periph_clock_t clock)168 void MXC_SYS_ClockEnable(mxc_sys_periph_clock_t clock)
169 {
170 if (clock > 63) {
171 clock -= 64;
172 MXC_MCR->pclkdis &= ~(0x1 << clock);
173 } else if (clock > 31) {
174 clock -= 32;
175 MXC_GCR->pclkdis1 &= ~(0x1 << clock);
176 } else {
177 MXC_GCR->pclkdis0 &= ~(0x1 << clock);
178 }
179 }
180 /* ************************************************************************** */
MXC_SYS_RTCClockEnable()181 void MXC_SYS_RTCClockEnable()
182 {
183 MXC_MCR->clkctrl |= MXC_F_MCR_CLKCTRL_ERTCO_EN;
184 }
185
186 /* ************************************************************************** */
MXC_SYS_RTCClockDisable(void)187 int MXC_SYS_RTCClockDisable(void)
188 {
189 /* Check that the RTC is not the system clock source */
190 if ((MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_SEL) != MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ERTCO) {
191 MXC_MCR->clkctrl &= ~MXC_F_MCR_CLKCTRL_ERTCO_EN;
192 return E_NO_ERROR;
193 } else {
194 return E_BAD_STATE;
195 }
196 }
197
198 /******************************************************************************/
MXC_SYS_ClockSourceEnable(mxc_sys_system_clock_t clock)199 int MXC_SYS_ClockSourceEnable(mxc_sys_system_clock_t clock)
200 {
201 int err = E_NO_ERROR;
202 switch (clock) {
203 case MXC_SYS_CLOCK_IPO:
204 MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IPO_EN;
205 return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IPO_RDY);
206 break;
207
208 case MXC_SYS_CLOCK_IBRO:
209 MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IBRO_EN;
210 return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IBRO_RDY);
211 break;
212
213 case MXC_SYS_CLOCK_EXTCLK:
214 err = MXC_GPIO_Config(&gpio_cfg_hf_extclk);
215 if (err)
216 return err;
217 return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_EXTCLK_RDY);
218 break;
219
220 case MXC_SYS_CLOCK_INRO:
221 // The 80k clock is always enabled
222 return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_INRO_RDY);
223 break;
224
225 case MXC_SYS_CLOCK_ERFO:
226 MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_ERFO_EN;
227 return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ERFO_RDY);
228 break;
229
230 case MXC_SYS_CLOCK_ERTCO:
231 MXC_MCR->clkctrl &= ~MXC_F_MCR_CLKCTRL_ERTCO_PD;
232 MXC_MCR->clkctrl |= MXC_F_MCR_CLKCTRL_ERTCO_EN;
233 return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ERTCO_RDY);
234 break;
235
236 default:
237 return E_BAD_PARAM;
238 break;
239 }
240 }
241
242 /******************************************************************************/
MXC_SYS_ClockSourceDisable(mxc_sys_system_clock_t clock)243 int MXC_SYS_ClockSourceDisable(mxc_sys_system_clock_t clock)
244 {
245 uint32_t current_clock;
246
247 current_clock = MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_SEL;
248
249 // Don't turn off the clock we're running on
250 if (clock == current_clock) {
251 return E_BAD_PARAM;
252 }
253
254 switch (clock) {
255 case MXC_SYS_CLOCK_IPO:
256 MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_IPO_EN;
257 break;
258
259 case MXC_SYS_CLOCK_IBRO:
260 MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_IBRO_EN;
261 break;
262
263 case MXC_SYS_CLOCK_EXTCLK:
264 /*
265 There's not a great way to disable the external clock.
266 Deinitializing the GPIO here may have unintended consequences
267 for application code.
268 Selecting a different system clock source is sufficient
269 to "disable" the EXT_CLK source.
270 */
271 break;
272
273 case MXC_SYS_CLOCK_INRO:
274 // The 80k clock is always enabled
275 break;
276
277 case MXC_SYS_CLOCK_ERFO:
278 MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_ERFO_EN;
279 break;
280
281 case MXC_SYS_CLOCK_ERTCO:
282 MXC_MCR->clkctrl &= ~MXC_F_MCR_CLKCTRL_ERTCO_EN;
283 break;
284
285 default:
286 return E_BAD_PARAM;
287 }
288
289 return E_NO_ERROR;
290 }
291
292 /* ************************************************************************** */
MXC_SYS_Clock_Timeout(uint32_t ready)293 int MXC_SYS_Clock_Timeout(uint32_t ready)
294 {
295 // Start timeout, wait for ready
296 MXC_DelayAsync(MXC_SYS_CLOCK_TIMEOUT, NULL);
297
298 /* TODO: Timeout on clock switch, use this for untrimmed parts. */
299 while (!(MXC_GCR->clkctrl & ready)) {}
300 return E_NO_ERROR;
301
302 do {
303 if (MXC_GCR->clkctrl & ready) {
304 MXC_DelayAbort();
305 return E_NO_ERROR;
306 }
307 } while (MXC_DelayCheck() == E_BUSY);
308
309 return E_TIME_OUT;
310 }
311
312 /* ************************************************************************** */
MXC_SYS_Clock_Select(mxc_sys_system_clock_t clock)313 int MXC_SYS_Clock_Select(mxc_sys_system_clock_t clock)
314 {
315 uint32_t current_clock;
316 int err = E_NO_ERROR;
317
318 // Save the current system clock
319 current_clock = MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_SEL;
320
321 switch (clock) {
322 case MXC_SYS_CLOCK_IPO:
323
324 // Enable IPO clock
325 if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_IPO_EN)) {
326 MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IPO_EN;
327
328 // Check if IPO clock is ready
329 if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IPO_RDY) != E_NO_ERROR) {
330 return E_TIME_OUT;
331 }
332 }
333
334 // Set IPO clock as System Clock
335 MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
336 MXC_S_GCR_CLKCTRL_SYSCLK_SEL_IPO);
337
338 break;
339
340 case MXC_SYS_CLOCK_IBRO:
341
342 // Enable IBRO clock
343 if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_IBRO_EN)) {
344 MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IBRO_EN;
345
346 // Check if IBRO clock is ready
347 if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IBRO_RDY) != E_NO_ERROR) {
348 return E_TIME_OUT;
349 }
350 }
351
352 // Set IBRO clock as System Clock
353 MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
354 MXC_S_GCR_CLKCTRL_SYSCLK_SEL_IBRO);
355
356 break;
357
358 case MXC_SYS_CLOCK_EXTCLK:
359 // Enable clock if necessary
360 if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_EXTCLK_RDY) != E_NO_ERROR) {
361 err = MXC_SYS_ClockSourceEnable(MXC_SYS_CLOCK_EXTCLK);
362 if (err)
363 return err;
364 }
365
366 MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
367 MXC_S_GCR_CLKCTRL_SYSCLK_SEL_EXTCLK);
368
369 break;
370
371 case MXC_SYS_CLOCK_ERFO:
372
373 // Enable ERFO clock
374 if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_ERFO_EN)) {
375 MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_ERFO_EN;
376
377 // Check if ERFO clock is ready
378 if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ERFO_RDY) != E_NO_ERROR) {
379 return E_TIME_OUT;
380 }
381 }
382
383 // Set ERFO clock as System Clock
384 MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
385 MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ERFO);
386
387 break;
388
389 case MXC_SYS_CLOCK_INRO:
390 // Set INRO clock as System Clock
391 MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
392 MXC_S_GCR_CLKCTRL_SYSCLK_SEL_INRO);
393
394 break;
395
396 case MXC_SYS_CLOCK_ERTCO:
397
398 // Enable ERTCO clock
399 if (!(MXC_MCR->clkctrl & MXC_F_MCR_CLKCTRL_ERTCO_EN)) {
400 MXC_MCR->clkctrl |= MXC_F_MCR_CLKCTRL_ERTCO_EN;
401
402 // Check if ERTCO clock is ready
403 if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ERTCO_RDY) != E_NO_ERROR) {
404 return E_TIME_OUT;
405 }
406 }
407
408 // Set ERTCO clock as System Clock
409 MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
410 MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ERTCO);
411
412 break;
413
414 default:
415 return E_BAD_PARAM;
416 }
417
418 // Wait for system clock to be ready
419 if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_SYSCLK_RDY) != E_NO_ERROR) {
420 // Restore the old system clock if timeout
421 MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL, current_clock);
422
423 return E_TIME_OUT;
424 }
425
426 // Update the system core clock
427 SystemCoreClockUpdate();
428
429 return E_NO_ERROR;
430 }
431
432 /* ************************************************************************** */
MXC_SYS_SetClockDiv(mxc_sys_system_clock_div_t div)433 void MXC_SYS_SetClockDiv(mxc_sys_system_clock_div_t div)
434 {
435 /* Return if this setting is already current */
436 if (div == MXC_SYS_GetClockDiv()) {
437 return;
438 }
439
440 MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_DIV, div);
441
442 SystemCoreClockUpdate();
443 }
444
445 /* ************************************************************************** */
MXC_SYS_GetClockDiv(void)446 mxc_sys_system_clock_div_t MXC_SYS_GetClockDiv(void)
447 {
448 return (MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_DIV);
449 }
450
451 /* ************************************************************************** */
MXC_SYS_Reset_Periph(mxc_sys_reset_t reset)452 void MXC_SYS_Reset_Periph(mxc_sys_reset_t reset)
453 {
454 if (reset > 31) {
455 reset -= 32;
456 MXC_GCR->rst1 = (0x1 << reset);
457 } else if (reset > 64) {
458 reset -= 64;
459 MXC_MCR->rst = (0x1 << reset);
460 } else {
461 MXC_GCR->rst0 = (0x1 << reset);
462 }
463 }
464
465 /* ************************************************************************** */
MXC_SYS_LockDAP_Permanent(void)466 int MXC_SYS_LockDAP_Permanent(void)
467 {
468 #ifdef DEBUG
469 // Locking the DAP is not supported while in DEBUG.
470 // To use this function, build for release ("make release")
471 // or set DEBUG = 0
472 // (see https://analogdevicesinc.github.io/msdk/USERGUIDE/#build-tables)
473 return E_NOT_SUPPORTED;
474 #else
475 int err;
476 uint32_t info_blk_addr;
477 uint32_t lock_sequence[4];
478
479 // Infoblock address to write lock sequence to
480 info_blk_addr = MXC_INFO_MEM_BASE + INFOBLOCK_DAP_LOCK_OFFSET;
481
482 // Set lock sequence
483 lock_sequence[0] = DAP_LOCK_SEQUENCE;
484 lock_sequence[1] = DAP_LOCK_SEQUENCE;
485 lock_sequence[2] = DAP_LOCK_SEQUENCE;
486 lock_sequence[3] = DAP_LOCK_SEQUENCE;
487
488 // Initialize FLC
489 MXC_FLC_Init();
490
491 // Unlock infoblock
492 MXC_FLC_UnlockInfoBlock(info_blk_addr);
493
494 // Write DAP lock sequence to infoblock
495 err = MXC_FLC_Write128(info_blk_addr, lock_sequence);
496
497 // Re-lock infoblock
498 MXC_FLC_LockInfoBlock(info_blk_addr);
499
500 return err;
501 #endif
502 }
503
504 /**@} end of mxc_sys */
505