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 "flc.h"
34 #include "tpu.h"
35 #include "mxc_delay.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 MXC_DELAY_MSEC(5)
47 #define MXC_I2C0 MXC_I2C0_BUS0
48 #define MXC_I2C1 MXC_I2C1_BUS0
49 #define MXC_I2C2 MXC_I2C2_BUS0
50
51 // DAP Lock macros
52 #define INFOBLOCK_DAP_LOCK_OFFSET 0x30
53 #define DAP_LOCK_SEQUENCE 0x5A5AA5A5
54
55 /* **** Globals **** */
56
57 /* **** Functions **** */
58
59 /* ************************************************************************** */
MXC_SYS_GetUSN(uint8_t * usn,uint8_t * checksum)60 int MXC_SYS_GetUSN(uint8_t *usn, uint8_t *checksum)
61 {
62 uint32_t *infoblock = (uint32_t *)MXC_INFO0_MEM_BASE;
63
64 /* Read the USN from the info block */
65 MXC_FLC_UnlockInfoBlock(MXC_INFO0_MEM_BASE);
66
67 memset(usn, 0, MXC_SYS_USN_CHECKSUM_LEN);
68
69 usn[0] = (infoblock[0] & 0x007F8000) >> 15;
70 usn[1] = (infoblock[0] & 0x7F800000) >> 23;
71 usn[2] = (infoblock[1] & 0x0000007F) << 1;
72 usn[2] |= (infoblock[0] & 0x80000000) >> 31;
73 usn[3] = (infoblock[1] & 0x00007F80) >> 7;
74 usn[4] = (infoblock[1] & 0x007F8000) >> 15;
75 usn[5] = (infoblock[1] & 0x7F800000) >> 23;
76 usn[6] = (infoblock[2] & 0x007F8000) >> 15;
77 usn[7] = (infoblock[2] & 0x7F800000) >> 23;
78 usn[8] = (infoblock[3] & 0x0000007F) << 1;
79 usn[8] |= (infoblock[2] & 0x80000000) >> 31;
80 usn[9] = (infoblock[3] & 0x00007F80) >> 7;
81 usn[10] = (infoblock[3] & 0x007F8000) >> 15;
82
83 // Compute the checksum
84 if (checksum != NULL) {
85 uint8_t check_csum[MXC_SYS_USN_CHECKSUM_LEN];
86 uint8_t key[MXC_SYS_USN_CHECKSUM_LEN];
87
88 /* Initialize the remainder of the USN and key */
89 memset(key, 0, MXC_SYS_USN_CHECKSUM_LEN);
90 memset(checksum, 0, MXC_SYS_USN_CSUM_FIELD_LEN);
91
92 /* Read the checksum from the info block */
93 checksum[1] = ((infoblock[3] & 0x7F800000) >> 23);
94 checksum[0] = ((infoblock[4] & 0x007F8000) >> 15);
95
96 MXC_TPU_Cipher_Config(MXC_TPU_MODE_ECB, MXC_TPU_CIPHER_AES128);
97 MXC_TPU_Cipher_AES_Encrypt((const char *)usn, NULL, (const char *)key,
98 MXC_TPU_CIPHER_AES128, MXC_TPU_MODE_ECB, MXC_AES_DATA_LEN,
99 (char *)check_csum);
100
101 /* Verify the checksum */
102 if ((checksum[0] != check_csum[0]) || (checksum[1] != check_csum[1])) {
103 MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
104 return E_UNKNOWN;
105 }
106 }
107
108 /* Add the info block checksum to the USN */
109 usn[11] = ((infoblock[3] & 0x7F800000) >> 23);
110 usn[12] = ((infoblock[4] & 0x007F8000) >> 15);
111
112 MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE);
113
114 return E_NO_ERROR;
115 }
116
117 /* ************************************************************************** */
MXC_SYS_IsClockEnabled(mxc_sys_periph_clock_t clock)118 int MXC_SYS_IsClockEnabled(mxc_sys_periph_clock_t clock)
119 {
120 /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 for the perckcn1 register. */
121 if (clock > 31) {
122 clock -= 32;
123 return !(MXC_GCR->perckcn1 & (0x1 << clock));
124 } else {
125 return !(MXC_GCR->perckcn0 & (0x1 << clock));
126 }
127 }
128
129 /* ************************************************************************** */
MXC_SYS_ClockDisable(mxc_sys_periph_clock_t clock)130 void MXC_SYS_ClockDisable(mxc_sys_periph_clock_t clock)
131 {
132 /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 for the perckcn1 register. */
133 if (clock > 31) {
134 clock -= 32;
135 MXC_GCR->perckcn1 |= (0x1 << clock);
136 } else {
137 MXC_GCR->perckcn0 |= (0x1 << clock);
138 }
139 }
140
141 /* ************************************************************************** */
MXC_SYS_ClockEnable(mxc_sys_periph_clock_t clock)142 void MXC_SYS_ClockEnable(mxc_sys_periph_clock_t clock)
143 {
144 /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 for the perckcn1 register. */
145 if (clock > 31) {
146 clock -= 32;
147 MXC_GCR->perckcn1 &= ~(0x1 << clock);
148 } else {
149 MXC_GCR->perckcn0 &= ~(0x1 << clock);
150 }
151 }
152 /* ************************************************************************** */
MXC_SYS_RTCClockEnable()153 void MXC_SYS_RTCClockEnable()
154 {
155 MXC_GCR->clkcn |= MXC_F_GCR_CLKCN_X32K_EN;
156 }
157
158 /* ************************************************************************** */
MXC_SYS_RTCClockDisable(void)159 int MXC_SYS_RTCClockDisable(void)
160 {
161 /* Check that the RTC is not the system clock source */
162 if ((MXC_GCR->clkcn & MXC_F_GCR_CLKCN_CLKSEL) != MXC_S_GCR_CLKCN_CLKSEL_XTAL32K) {
163 MXC_GCR->clkcn &= ~MXC_F_GCR_CLKCN_X32K_EN;
164 return E_NO_ERROR;
165 } else {
166 return E_BAD_STATE;
167 }
168 }
169
170 /******************************************************************************/
MXC_SYS_ClockSourceEnable(mxc_sys_system_clock_t clock)171 int MXC_SYS_ClockSourceEnable(mxc_sys_system_clock_t clock)
172 {
173 switch (clock) {
174 case MXC_SYS_CLOCK_HIRC96:
175 MXC_GCR->clkcn |= MXC_F_GCR_CLKCN_HIRC96M_EN;
176 return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCN_HIRC96M_RDY);
177 break;
178 case MXC_SYS_CLOCK_HIRC8:
179 MXC_GCR->clkcn |= MXC_F_GCR_CLKCN_HIRC8M_EN;
180 return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCN_HIRC8M_RDY);
181 break;
182 case MXC_SYS_CLOCK_HIRC:
183 MXC_GCR->clkcn |= MXC_F_GCR_CLKCN_HIRC_EN;
184 return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCN_HIRC_RDY);
185 break;
186 case MXC_SYS_CLOCK_LIRC8K:
187 // The 8k clock is always enabled
188 return E_NO_ERROR;
189 break;
190 case MXC_SYS_CLOCK_XTAL32M:
191 MXC_GCR->btle_ldocr |= MXC_F_GCR_BTLE_LDOCR_LDORXEN | MXC_F_GCR_BTLE_LDOCR_LDOTXEN;
192 MXC_GCR->clkcn |= MXC_F_GCR_CLKCN_X32M_EN;
193 return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCN_X32M_RDY);
194 break;
195 case MXC_SYS_CLOCK_XTAL32K:
196 MXC_GCR->clkcn |= MXC_F_GCR_CLKCN_X32K_EN;
197 return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCN_X32K_RDY);
198 break;
199 default:
200 return E_BAD_PARAM;
201 break;
202 }
203 }
204
205 /******************************************************************************/
MXC_SYS_ClockSourceDisable(mxc_sys_system_clock_t clock)206 int MXC_SYS_ClockSourceDisable(mxc_sys_system_clock_t clock)
207 {
208 uint32_t current_clock;
209
210 current_clock = MXC_GCR->clkcn & MXC_F_GCR_CLKCN_CLKSEL;
211
212 // Don't turn off the clock we're running on
213 if (clock == current_clock) {
214 return E_BAD_PARAM;
215 }
216
217 switch (clock) {
218 case MXC_SYS_CLOCK_HIRC96:
219 MXC_GCR->clkcn &= ~MXC_F_GCR_CLKCN_HIRC96M_EN;
220 break;
221 case MXC_SYS_CLOCK_HIRC8:
222 MXC_GCR->clkcn &= ~MXC_F_GCR_CLKCN_HIRC8M_EN;
223 break;
224 case MXC_SYS_CLOCK_HIRC:
225 MXC_GCR->clkcn &= ~MXC_F_GCR_CLKCN_HIRC_EN;
226 break;
227 case MXC_SYS_CLOCK_LIRC8K:
228 // The 8k clock is always enabled
229 break;
230 case MXC_SYS_CLOCK_XTAL32M:
231 MXC_GCR->clkcn &= ~MXC_F_GCR_CLKCN_X32M_EN;
232 break;
233 case MXC_SYS_CLOCK_XTAL32K:
234 MXC_GCR->clkcn &= ~MXC_F_GCR_CLKCN_X32K_EN;
235 break;
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->clkcn & 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->clkcn & MXC_F_GCR_CLKCN_CLKSEL;
266
267 switch (clock) {
268 case MXC_SYS_CLOCK_HIRC96:
269 // Enable HIRC96 clock
270 if (!(MXC_GCR->clkcn & MXC_F_GCR_CLKCN_HIRC96M_EN)) {
271 MXC_GCR->clkcn |= MXC_F_GCR_CLKCN_HIRC96M_EN;
272
273 // Check if HIRC96 clock is ready
274 if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCN_HIRC96M_RDY) != E_NO_ERROR) {
275 return E_TIME_OUT;
276 }
277 }
278
279 // Set HIRC96 clock as System Clock
280 MXC_SETFIELD(MXC_GCR->clkcn, MXC_F_GCR_CLKCN_CLKSEL, MXC_S_GCR_CLKCN_CLKSEL_HIRC96);
281
282 break;
283 case MXC_SYS_CLOCK_HIRC8:
284 // Enable HIRC8 clock
285 if (!(MXC_GCR->clkcn & MXC_F_GCR_CLKCN_HIRC8M_EN)) {
286 MXC_GCR->clkcn |= MXC_F_GCR_CLKCN_HIRC8M_EN;
287
288 // Check if HIRC8 clock is ready
289 if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCN_HIRC8M_RDY) != E_NO_ERROR) {
290 return E_TIME_OUT;
291 }
292 }
293
294 // Set HIRC8 clock as System Clock
295 MXC_SETFIELD(MXC_GCR->clkcn, MXC_F_GCR_CLKCN_CLKSEL, MXC_S_GCR_CLKCN_CLKSEL_HIRC8);
296
297 break;
298 case MXC_SYS_CLOCK_HIRC:
299 // Enable HIRC clock
300 if (!(MXC_GCR->clkcn & MXC_F_GCR_CLKCN_HIRC_EN)) {
301 MXC_GCR->clkcn |= MXC_F_GCR_CLKCN_HIRC_EN;
302
303 // Check if HIRC clock is ready
304 if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCN_HIRC_RDY) != E_NO_ERROR) {
305 return E_TIME_OUT;
306 }
307 }
308
309 // Set HIRC clock as System Clock
310 MXC_SETFIELD(MXC_GCR->clkcn, MXC_F_GCR_CLKCN_CLKSEL, MXC_S_GCR_CLKCN_CLKSEL_HIRC);
311
312 break;
313 case MXC_SYS_CLOCK_XTAL32M:
314 // Enable XTAL32M clock
315 if (!(MXC_GCR->clkcn & MXC_F_GCR_CLKCN_X32M_EN)) {
316 MXC_GCR->btle_ldocr |= MXC_F_GCR_BTLE_LDOCR_LDORXEN | MXC_F_GCR_BTLE_LDOCR_LDOTXEN;
317 MXC_GCR->clkcn |= MXC_F_GCR_CLKCN_X32M_EN;
318
319 // Check if XTAL32M clock is ready
320 if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCN_X32M_RDY) != E_NO_ERROR) {
321 return E_TIME_OUT;
322 }
323 }
324
325 // Set XTAL32M clock as System Clock
326 MXC_SETFIELD(MXC_GCR->clkcn, MXC_F_GCR_CLKCN_CLKSEL, MXC_S_GCR_CLKCN_CLKSEL_XTAL32M);
327
328 break;
329 case MXC_SYS_CLOCK_LIRC8K:
330 // Set LIRC8 clock as System Clock
331 MXC_SETFIELD(MXC_GCR->clkcn, MXC_F_GCR_CLKCN_CLKSEL, MXC_S_GCR_CLKCN_CLKSEL_LIRC8);
332
333 break;
334 case MXC_SYS_CLOCK_XTAL32K:
335 // Enable XTAL32K clock
336 if (!(MXC_GCR->clkcn & MXC_F_GCR_CLKCN_X32K_EN)) {
337 MXC_GCR->clkcn |= MXC_F_GCR_CLKCN_X32K_EN;
338
339 // Check if XTAL32K clock is ready
340 if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCN_X32K_RDY) != E_NO_ERROR) {
341 return E_TIME_OUT;
342 }
343 }
344
345 // Set XTAL32K clock as System Clock
346 MXC_SETFIELD(MXC_GCR->clkcn, MXC_F_GCR_CLKCN_CLKSEL, MXC_S_GCR_CLKCN_CLKSEL_XTAL32K);
347
348 break;
349 default:
350 return E_BAD_PARAM;
351 }
352
353 // Wait for system clock to be ready
354 if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCN_CKRDY) != E_NO_ERROR) {
355 // Restore the old system clock if timeout
356 MXC_SETFIELD(MXC_GCR->clkcn, MXC_F_GCR_CLKCN_CLKSEL, current_clock);
357
358 return E_TIME_OUT;
359 }
360
361 // Update the system core clock
362 SystemCoreClockUpdate();
363
364 return E_NO_ERROR;
365 }
366
367 /* ************************************************************************** */
MXC_SYS_Clock_Div(mxc_sys_system_div_t div)368 void MXC_SYS_Clock_Div(mxc_sys_system_div_t div)
369 {
370 MXC_SETFIELD(MXC_GCR->clkcn, MXC_F_GCR_CLKCN_PSC, div);
371
372 // Update the system core clock
373 SystemCoreClockUpdate();
374 }
375
376 /* ************************************************************************** */
MXC_SYS_Reset_Periph(mxc_sys_reset_t reset)377 void MXC_SYS_Reset_Periph(mxc_sys_reset_t reset)
378 {
379 /* The mxc_sys_reset_t enum uses enum values that are the offset by 32 for the rstr1 register. */
380 if (reset > 31) {
381 reset -= 32;
382 MXC_GCR->rstr1 = (0x1 << reset);
383 while (MXC_GCR->rstr1 & (0x1 << reset)) {}
384 } else {
385 MXC_GCR->rstr0 = (0x1 << reset);
386 while (MXC_GCR->rstr0 & (0x1 << reset)) {}
387 }
388 }
389
390 /* ************************************************************************** */
MXC_SYS_GetRev(void)391 uint8_t MXC_SYS_GetRev(void)
392 {
393 uint8_t serialNumber[MXC_SYS_USN_CHECKSUM_LEN];
394 MXC_SYS_GetUSN(serialNumber, NULL);
395
396 if ((serialNumber[0] < 0x9F) | ((serialNumber[0] & 0x0F) > 0x09)) {
397 // Fail back to the hardware register
398 return MXC_GCR->revision & 0xFF;
399 }
400
401 return serialNumber[0];
402 }
403
404 /* ************************************************************************** */
MXC_SYS_LockDAP_Permanent(void)405 int MXC_SYS_LockDAP_Permanent(void)
406 {
407 #ifdef DEBUG
408 // Locking the DAP is not supported while in DEBUG.
409 // To use this function, build for release ("make release")
410 // or set DEBUG = 0
411 // (see https://analogdevicesinc.github.io/msdk/USERGUIDE/#build-tables)
412 return E_NOT_SUPPORTED;
413 #else
414 int err;
415 uint32_t info_blk_addr;
416 uint32_t lock_sequence[4];
417
418 // Infoblock address to write lock sequence to
419 info_blk_addr = MXC_INFO_MEM_BASE + INFOBLOCK_DAP_LOCK_OFFSET;
420
421 // Set lock sequence
422 lock_sequence[0] = DAP_LOCK_SEQUENCE;
423 lock_sequence[1] = DAP_LOCK_SEQUENCE;
424 lock_sequence[2] = DAP_LOCK_SEQUENCE;
425 lock_sequence[3] = DAP_LOCK_SEQUENCE;
426
427 // Initialize FLC
428 MXC_FLC_Init();
429
430 // Unlock infoblock
431 MXC_FLC_UnlockInfoBlock(info_blk_addr);
432
433 // Write DAP lock sequence to infoblock
434 err = MXC_FLC_Write128(info_blk_addr, lock_sequence);
435
436 // Re-lock infoblock
437 MXC_FLC_LockInfoBlock(info_blk_addr);
438
439 return err;
440 #endif
441 }
442
443 /**@} end of mxc_sys */
444