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