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(1)
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 
91 /******************************************************************************/
MXC_SYS_ClockSourceEnable(mxc_sys_system_clock_t clock)92 int MXC_SYS_ClockSourceEnable(mxc_sys_system_clock_t clock)
93 {
94     switch (clock) {
95     case MXC_SYS_CLOCK_IPO:
96         MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IPO_EN;
97         return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IPO_RDY);
98         break;
99 
100     case MXC_SYS_CLOCK_IBRO:
101         MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IBRO_EN;
102         return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IBRO_RDY);
103         break;
104 
105     case MXC_SYS_CLOCK_INRO:
106         // The nano ring clock is always enabled
107         return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_INRO_RDY);
108         break;
109 
110     default:
111         return E_BAD_PARAM;
112         break;
113     }
114 }
115 
116 /******************************************************************************/
MXC_SYS_ClockSourceDisable(mxc_sys_system_clock_t clock)117 int MXC_SYS_ClockSourceDisable(mxc_sys_system_clock_t clock)
118 {
119     uint32_t current_clock;
120 
121     current_clock = MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_SEL;
122 
123     // Don't turn off the clock we're running on
124     if (clock == current_clock) {
125         return E_BAD_PARAM;
126     }
127 
128     switch (clock) {
129     case MXC_SYS_CLOCK_IPO:
130         MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_IPO_EN;
131         break;
132 
133     case MXC_SYS_CLOCK_IBRO:
134         MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_IBRO_EN;
135         break;
136 
137     case MXC_SYS_CLOCK_INRO:
138         // The 8k clock is always enabled
139         break;
140 
141     default:
142         return E_BAD_PARAM;
143     }
144 
145     return E_NO_ERROR;
146 }
147 
148 /* ************************************************************************** */
MXC_SYS_Clock_Timeout(uint32_t ready)149 int MXC_SYS_Clock_Timeout(uint32_t ready)
150 {
151     // Start timeout, wait for ready
152     MXC_DelayAsync(MXC_SYS_CLOCK_TIMEOUT, NULL);
153 
154     do {
155         if (MXC_GCR->clkctrl & ready) {
156             MXC_DelayAbort();
157             return E_NO_ERROR;
158         }
159     } while (MXC_DelayCheck() == E_BUSY);
160 
161     return E_TIME_OUT;
162 }
163 
164 /* ************************************************************************** */
MXC_SYS_Clock_Select(mxc_sys_system_clock_t clock)165 int MXC_SYS_Clock_Select(mxc_sys_system_clock_t clock)
166 {
167     uint32_t current_clock;
168 
169     // Save the current system clock
170     current_clock = MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_SEL;
171 
172     switch (clock) {
173     case MXC_SYS_CLOCK_IPO:
174 
175         // Enable IPO clock
176         if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_IPO_EN)) {
177             MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IPO_EN;
178 
179             // Check if IPO clock is ready
180             if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IPO_RDY) != E_NO_ERROR) {
181                 return E_TIME_OUT;
182             }
183         }
184 
185         // Set IPO clock as System Clock
186         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
187                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_IPO);
188 
189         break;
190 
191     case MXC_SYS_CLOCK_IBRO:
192 
193         // Enable IBRO clock
194         if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_IBRO_EN)) {
195             MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IBRO_EN;
196 
197             // Check if IBRO clock is ready
198             if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IBRO_RDY) != E_NO_ERROR) {
199                 return E_TIME_OUT;
200             }
201         }
202 
203         // Set IBRO clock as System Clock
204         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
205                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_IBRO);
206 
207         break;
208 
209     case MXC_SYS_CLOCK_INRO:
210         // Set INRO clock as System Clock
211         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL,
212                      MXC_S_GCR_CLKCTRL_SYSCLK_SEL_INRO);
213 
214         break;
215 
216     default:
217         return E_BAD_PARAM;
218     }
219 
220     // Wait for system clock to be ready
221     if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_SYSCLK_RDY) != E_NO_ERROR) {
222         // Restore the old system clock if timeout
223         MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL, current_clock);
224 
225         return E_TIME_OUT;
226     }
227 
228     // Update the system core clock
229     SystemCoreClockUpdate();
230 
231     return E_NO_ERROR;
232 }
233 
234 /* ************************************************************************** */
MXC_SYS_SetClockDiv(mxc_sys_system_clock_div_t div)235 void MXC_SYS_SetClockDiv(mxc_sys_system_clock_div_t div)
236 {
237     /* Return if this setting is already current */
238     if (div == MXC_SYS_GetClockDiv()) {
239         return;
240     }
241 
242     MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_DIV, div);
243 
244     SystemCoreClockUpdate();
245 }
246 
247 /* ************************************************************************** */
MXC_SYS_GetClockDiv(void)248 mxc_sys_system_clock_div_t MXC_SYS_GetClockDiv(void)
249 {
250     return (MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_DIV);
251 }
252 
253 /* ************************************************************************** */
MXC_SYS_Reset_Periph(mxc_sys_reset_t reset)254 void MXC_SYS_Reset_Periph(mxc_sys_reset_t reset)
255 {
256     /* The mxc_sys_reset_t enum uses enum values that are the offset by 32 for the rst1 register. */
257     if (reset > 31) {
258         reset -= 32;
259         MXC_GCR->rst1 = (0x1 << reset);
260         while (MXC_GCR->rst1 & (0x1 << reset)) {}
261     } else {
262         MXC_GCR->rst0 = (0x1 << reset);
263         while (MXC_GCR->rst0 & (0x1 << reset)) {}
264     }
265 }
266 
267 /* ************************************************************************** */
MXC_SYS_GetUSN(uint8_t * serialNumber,int len)268 int MXC_SYS_GetUSN(uint8_t *serialNumber, int len)
269 {
270     if (len != 13) {
271         return E_BAD_PARAM;
272     } else if (serialNumber == NULL) {
273         return E_NULL_PTR;
274     }
275 
276     uint32_t infoblock[6];
277 
278     MXC_FLC_UnlockInfoBlock(MXC_INFO_MEM_BASE);
279     infoblock[0] = *(uint32_t *)MXC_INFO_MEM_BASE;
280     infoblock[1] = *(uint32_t *)(MXC_INFO_MEM_BASE + 4);
281     infoblock[2] = *(uint32_t *)(MXC_INFO_MEM_BASE + 8);
282     infoblock[3] = *(uint32_t *)(MXC_INFO_MEM_BASE + 12);
283     infoblock[4] = *(uint32_t *)(MXC_INFO_MEM_BASE + 16);
284     infoblock[5] = *(uint32_t *)(MXC_INFO_MEM_BASE + 20);
285     MXC_FLC_LockInfoBlock(MXC_INFO_MEM_BASE);
286 
287     serialNumber[0] = (infoblock[0] & 0x007F8000) >> 15;
288     serialNumber[1] = (infoblock[0] & 0x7F800000) >> 23;
289     serialNumber[2] = (infoblock[0] & 0x80000000) >> 31;
290     serialNumber[2] |= (infoblock[1] & 0x0000007F) << 1;
291     serialNumber[3] = (infoblock[1] & 0x00007F80) >> 7;
292     serialNumber[4] = (infoblock[1] & 0x007F8000) >> 15;
293     serialNumber[5] = (infoblock[1] & 0x7F800000) >> 23;
294     serialNumber[6] = (infoblock[2] & 0x007F8000) >> 15;
295     serialNumber[7] = (infoblock[2] & 0x7F800000) >> 23;
296     serialNumber[8] = (infoblock[2] & 0x80000000) >> 31;
297     serialNumber[8] |= (infoblock[3] & 0x0000007F) << 1;
298     serialNumber[9] = (infoblock[3] & 0x00007F80) >> 7;
299     serialNumber[10] = (infoblock[3] & 0x007F8000) >> 15;
300     serialNumber[11] = (infoblock[3] & 0x7F800000) >> 23;
301     serialNumber[12] = (infoblock[4] & 0x007F8000) >> 15;
302 
303     return E_NO_ERROR;
304 }
305 
306 /* ************************************************************************** */
MXC_SYS_GetRev(void)307 uint8_t MXC_SYS_GetRev(void)
308 {
309     uint8_t serialNumber[13];
310     MXC_SYS_GetUSN(serialNumber, 13);
311 
312     if ((serialNumber[0] < 0x9F) | ((serialNumber[0] & 0x0F) > 0x09)) {
313         // Fail back to the hardware register
314         return MXC_GCR->revision & 0xFF;
315     }
316 
317     return serialNumber[0];
318 }
319 
320 /* ************************************************************************** */
MXC_SYS_LockDAP_Permanent(void)321 int MXC_SYS_LockDAP_Permanent(void)
322 {
323 #ifdef DEBUG
324     // Locking the DAP is not supported while in DEBUG.
325     // To use this function, build for release ("make release")
326     // or set DEBUG = 0
327     // (see https://analogdevicesinc.github.io/msdk/USERGUIDE/#build-tables)
328     return E_NOT_SUPPORTED;
329 #else
330     int err;
331     uint32_t info_blk_addr;
332     uint32_t lock_sequence[4];
333 
334     // Infoblock address to write lock sequence to
335     info_blk_addr = MXC_INFO_MEM_BASE + INFOBLOCK_DAP_LOCK_OFFSET;
336 
337     // Set lock sequence
338     lock_sequence[0] = DAP_LOCK_SEQUENCE_01;
339     lock_sequence[1] = DAP_LOCK_SEQUENCE_01;
340     lock_sequence[2] = DAP_LOCK_SEQUENCE_23;
341     lock_sequence[3] = DAP_LOCK_SEQUENCE_23;
342 
343     // Initialize FLC
344     MXC_FLC_Init();
345 
346     // Unlock infoblock
347     MXC_FLC_UnlockInfoBlock(info_blk_addr);
348 
349     // Write DAP lock sequence to infoblock
350     err = MXC_FLC_Write128(info_blk_addr, lock_sequence);
351 
352     // Re-lock infoblock
353     MXC_FLC_LockInfoBlock(info_blk_addr);
354 
355     return err;
356 #endif
357 }
358 
359 /**@} end of mxc_sys */
360