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