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_errors.h"
30 #include "mxc_device.h"
31 #include "mxc_assert.h"
32 #include "mxc_sys.h"
33 #include "mxc_delay.h"
34 #include "flc.h"
35 #include "gcr_regs.h"
36 #include "fcr_regs.h"
37 
38 /**
39  * @ingroup mxc_sys
40  * @{
41  */
42 
43 /* **** Definitions **** */
44 #define MXC_SYS_CLOCK_TIMEOUT MXC_DELAY_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_GetUSN(uint8_t * usn,int len,int part)56 int MXC_SYS_GetUSN(uint8_t *usn, int len, int part)
57 {
58     if (len != MXC_SYS_USN_LEN) {
59         return E_BAD_PARAM;
60     } else if (usn == NULL) {
61         return E_NULL_PTR;
62     }
63 
64     uint32_t infoblock[6];
65 
66     MXC_FLC_UnlockInfoBlock(MXC_INFO_MEM_BASE);
67     infoblock[0] = *(uint32_t *)MXC_INFO_MEM_BASE;
68     infoblock[1] = *(uint32_t *)(MXC_INFO_MEM_BASE + 4);
69     infoblock[2] = *(uint32_t *)(MXC_INFO_MEM_BASE + 8);
70     infoblock[3] = *(uint32_t *)(MXC_INFO_MEM_BASE + 12);
71     infoblock[4] = *(uint32_t *)(MXC_INFO_MEM_BASE + 16);
72     infoblock[5] = *(uint32_t *)(MXC_INFO_MEM_BASE + 20);
73     MXC_FLC_LockInfoBlock(MXC_INFO_MEM_BASE);
74 
75     if (part == 0) {
76         usn[0] = (infoblock[0] & 0x000000FF);
77         usn[1] = (infoblock[0] & 0x0000FF00) >> 8;
78         usn[2] = (infoblock[0] & 0x00FF0000) >> 16;
79         usn[3] = (infoblock[0] & 0x3F000000) >> 24;
80         usn[3] |= (infoblock[1] & 0x00000003) << 6;
81         usn[4] = (infoblock[1] & 0x000003FC) >> 2;
82         usn[5] = (infoblock[1] & 0x0003FC00) >> 10;
83         usn[6] = (infoblock[1] & 0x03FC0000) >> 18;
84         usn[7] = (infoblock[1] & 0x3C000000) >> 26;
85     } else if (part == 1) {
86         usn[0] = (infoblock[2] & 0x000000FF);
87         usn[1] = (infoblock[2] & 0x0000FF00) >> 8;
88         usn[2] = (infoblock[2] & 0x00FF0000) >> 16;
89         usn[3] = (infoblock[2] & 0xFF000000) >> 24;
90         usn[4] = (infoblock[3] & 0x000000FF);
91         usn[5] = (infoblock[3] & 0x0000FF00) >> 8;
92         usn[6] = (infoblock[3] & 0x00FF0000) >> 16;
93         usn[7] = (infoblock[3] & 0xFF000000) >> 24;
94     } else if (part == 2) {
95         usn[0] = (infoblock[4] & 0x000000FF);
96         usn[1] = (infoblock[4] & 0x0000FF00) >> 8;
97         usn[2] = (infoblock[4] & 0x00FF0000) >> 16;
98         usn[3] = (infoblock[4] & 0xFF000000) >> 24;
99         usn[4] = (infoblock[5] & 0x000000FF);
100         usn[5] = (infoblock[5] & 0x0000FF00) >> 8;
101         usn[6] = (infoblock[5] & 0x00FF0000) >> 16;
102         usn[7] = (infoblock[5] & 0xFF000000) >> 24;
103     } else {
104         return E_BAD_PARAM;
105     }
106 
107     return E_NO_ERROR;
108 }
109 
110 /* ************************************************************************** */
MXC_SYS_IsClockEnabled(mxc_sys_periph_clock_t clock)111 int MXC_SYS_IsClockEnabled(mxc_sys_periph_clock_t clock)
112 {
113     /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 for the pclkdis1 register. */
114     if (clock > 31) {
115         clock -= 32;
116         return !(MXC_GCR->pclk_dis1 & (0x1 << clock));
117     } else {
118         return !(MXC_GCR->pclk_dis0 & (0x1 << clock));
119     }
120 }
121 
122 /* ************************************************************************** */
MXC_SYS_ClockDisable(mxc_sys_periph_clock_t clock)123 void MXC_SYS_ClockDisable(mxc_sys_periph_clock_t clock)
124 {
125     /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 for the pclkdis1 register. */
126     if (clock > 31) {
127         clock -= 32;
128         MXC_GCR->pclk_dis1 |= (0x1 << clock);
129     } else {
130         MXC_GCR->pclk_dis0 |= (0x1 << clock);
131     }
132 }
133 
134 /* ************************************************************************** */
MXC_SYS_ClockEnable(mxc_sys_periph_clock_t clock)135 void MXC_SYS_ClockEnable(mxc_sys_periph_clock_t clock)
136 {
137     /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 for the pclkdis1 register. */
138     if (clock > 31) {
139         clock -= 32;
140         MXC_GCR->pclk_dis1 &= ~(0x1 << clock);
141     } else {
142         MXC_GCR->pclk_dis0 &= ~(0x1 << clock);
143     }
144 }
145 /* ************************************************************************** */
MXC_SYS_RTCClockEnable()146 void MXC_SYS_RTCClockEnable()
147 {
148     MXC_GCR->clk_ctrl |= MXC_F_GCR_CLK_CTRL_X32K_EN;
149 }
150 
151 /* ************************************************************************** */
MXC_SYS_RTCClockDisable(void)152 int MXC_SYS_RTCClockDisable(void)
153 {
154     /* Check that the RTC is not the system clock source */
155     if ((MXC_GCR->clk_ctrl & MXC_F_GCR_CLK_CTRL_CLKSEL) != MXC_S_GCR_CLK_CTRL_CLKSEL_HFXIN) {
156         MXC_GCR->clk_ctrl &= ~MXC_F_GCR_CLK_CTRL_X32K_EN;
157         return E_NO_ERROR;
158     } else {
159         return E_BAD_STATE;
160     }
161 }
162 
163 /******************************************************************************/
MXC_SYS_ClockSourceEnable(mxc_sys_system_clock_t clock)164 int MXC_SYS_ClockSourceEnable(mxc_sys_system_clock_t clock)
165 {
166     switch (clock) {
167     case MXC_SYS_CLOCK_HIRC:
168         MXC_GCR->clk_ctrl |= MXC_F_GCR_CLK_CTRL_HIRC_EN;
169         return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLK_CTRL_HIRC_RDY);
170         break;
171 
172     case MXC_SYS_CLOCK_HFXIN:
173         MXC_GCR->clk_ctrl |= MXC_F_GCR_CLK_CTRL_X32K_EN;
174         return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLK_CTRL_X32K_RDY);
175         break;
176 
177     case MXC_SYS_CLOCK_NANORING:
178         // 80khz nanoring is always enabled
179         return E_NO_ERROR;
180         break;
181 
182     default:
183         return E_BAD_PARAM;
184         break;
185     }
186 }
187 
188 /******************************************************************************/
MXC_SYS_ClockSourceDisable(mxc_sys_system_clock_t clock)189 int MXC_SYS_ClockSourceDisable(mxc_sys_system_clock_t clock)
190 {
191     uint32_t current_clock;
192 
193     current_clock = MXC_GCR->clk_ctrl & MXC_F_GCR_CLK_CTRL_CLKSEL;
194 
195     // Don't turn off the clock we're running on
196     if (clock == current_clock) {
197         return E_BAD_PARAM;
198     }
199 
200     switch (clock) {
201     case MXC_SYS_CLOCK_HIRC:
202         MXC_GCR->clk_ctrl &= ~MXC_F_GCR_CLK_CTRL_HIRC_EN;
203         break;
204 
205     case MXC_SYS_CLOCK_HFXIN:
206         MXC_GCR->clk_ctrl &= ~MXC_F_GCR_CLK_CTRL_X32K_EN;
207         break;
208 
209     case MXC_SYS_CLOCK_NANORING:
210         // 80khz nanoring is always enabled
211         return E_BAD_PARAM;
212 
213     default:
214         return E_BAD_PARAM;
215     }
216 
217     return E_NO_ERROR;
218 }
219 
220 /* ************************************************************************** */
MXC_SYS_Clock_Timeout(uint32_t ready)221 int MXC_SYS_Clock_Timeout(uint32_t ready)
222 {
223     // Start timeout, wait for ready
224     MXC_DelayAsync(MXC_SYS_CLOCK_TIMEOUT, NULL);
225 
226     do {
227         if (MXC_GCR->clk_ctrl & ready) {
228             MXC_DelayAbort();
229             return E_NO_ERROR;
230         }
231     } while (MXC_DelayCheck() == E_BUSY);
232 
233     return E_TIME_OUT;
234 }
235 
236 /* ************************************************************************** */
MXC_SYS_Clock_Select(mxc_sys_system_clock_t clock)237 int MXC_SYS_Clock_Select(mxc_sys_system_clock_t clock)
238 {
239     uint32_t current_clock;
240 
241     // Save the current system clock
242     current_clock = MXC_GCR->clk_ctrl & MXC_F_GCR_CLK_CTRL_CLKSEL;
243 
244     switch (clock) {
245     case MXC_SYS_CLOCK_HIRC:
246 
247         // Enable HIRC clock
248         if (!(MXC_GCR->clk_ctrl & MXC_F_GCR_CLK_CTRL_HIRC_EN)) {
249             MXC_GCR->clk_ctrl |= MXC_F_GCR_CLK_CTRL_HIRC_EN;
250 
251             // Check if HIRC clock is ready
252             if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLK_CTRL_HIRC_RDY) != E_NO_ERROR) {
253                 return E_TIME_OUT;
254             }
255         }
256 
257         // Set HIRC clock as System Clock
258         MXC_SETFIELD(MXC_GCR->clk_ctrl, MXC_F_GCR_CLK_CTRL_CLKSEL, MXC_S_GCR_CLK_CTRL_CLKSEL_HIRC);
259 
260         break;
261 
262     case MXC_SYS_CLOCK_HFXIN:
263 
264         // Enable HFXtal clock
265         if (!(MXC_GCR->clk_ctrl & MXC_F_GCR_CLK_CTRL_X32K_EN)) {
266             MXC_GCR->clk_ctrl |= MXC_F_GCR_CLK_CTRL_X32K_EN;
267 
268             // Check if HFXtal clock is ready
269             if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLK_CTRL_X32K_RDY) != E_NO_ERROR) {
270                 return E_TIME_OUT;
271             }
272         }
273 
274         // Set HFXtal clock as System Clock
275         MXC_SETFIELD(MXC_GCR->clk_ctrl, MXC_F_GCR_CLK_CTRL_CLKSEL, MXC_S_GCR_CLK_CTRL_CLKSEL_HFXIN);
276 
277         break;
278 
279     case MXC_SYS_CLOCK_NANORING:
280         if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLK_CTRL_LIRC8K_RDY) != E_NO_ERROR) {
281             return E_TIME_OUT;
282         }
283 
284         MXC_SETFIELD(MXC_GCR->clk_ctrl, MXC_F_GCR_CLK_CTRL_CLKSEL,
285                      MXC_S_GCR_CLK_CTRL_CLKSEL_NANORING);
286 
287         break;
288 
289     default:
290         return E_BAD_PARAM;
291     }
292 
293     // Wait for system clock to be ready
294     if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLK_CTRL_CLKRDY) != E_NO_ERROR) {
295         // Restore the old system clock if timeout
296         MXC_SETFIELD(MXC_GCR->clk_ctrl, MXC_F_GCR_CLK_CTRL_CLKSEL, current_clock);
297 
298         return E_TIME_OUT;
299     }
300 
301     // Update the system core clock
302     SystemCoreClockUpdate();
303 
304     return E_NO_ERROR;
305 }
306 
307 /* ************************************************************************** */
MXC_SYS_SetClockDiv(mxc_sys_system_clock_div_t div)308 void MXC_SYS_SetClockDiv(mxc_sys_system_clock_div_t div)
309 {
310     /* Return if this setting is already current */
311     if (div == MXC_SYS_GetClockDiv()) {
312         return;
313     }
314 
315     MXC_SETFIELD(MXC_GCR->clk_ctrl, MXC_F_GCR_CLK_CTRL_PSC, div);
316 
317     SystemCoreClockUpdate();
318 }
319 
320 /* ************************************************************************** */
MXC_SYS_GetClockDiv(void)321 mxc_sys_system_clock_div_t MXC_SYS_GetClockDiv(void)
322 {
323     return (MXC_GCR->clk_ctrl & MXC_F_GCR_CLK_CTRL_PSC);
324 }
325 
326 /* ************************************************************************** */
MXC_SYS_Reset_Periph(mxc_sys_reset_t reset)327 void MXC_SYS_Reset_Periph(mxc_sys_reset_t reset)
328 {
329     /* The mxc_sys_reset_t enum uses enum values that are the offset by 32 for the rstr1 register. */
330     if (reset > 31) {
331         reset -= 32;
332         MXC_GCR->rst1 = (0x1 << reset);
333         while (MXC_GCR->rst1 & (0x1 << reset)) {}
334     } else {
335         MXC_GCR->rst0 = (0x1 << reset);
336         while (MXC_GCR->rst0 & (0x1 << reset)) {}
337     }
338 }
339 
340 /* ************************************************************************** */
MXC_SYS_LockDAP_Permanent(void)341 int MXC_SYS_LockDAP_Permanent(void)
342 {
343 #ifdef DEBUG
344     // Locking the DAP is not supported while in DEBUG.
345     // To use this function, build for release ("make release")
346     // or set DEBUG = 0
347     // (see https://analogdevicesinc.github.io/msdk/USERGUIDE/#build-tables)
348     return E_NOT_SUPPORTED;
349 #else
350     int err;
351     uint32_t info_blk_addr;
352     uint32_t lock_sequence[4];
353 
354     // Infoblock address to write lock sequence to
355     info_blk_addr = MXC_INFO_MEM_BASE + INFOBLOCK_DAP_LOCK_OFFSET;
356 
357     // Set lock sequence
358     lock_sequence[0] = DAP_LOCK_SEQUENCE_01;
359     lock_sequence[1] = DAP_LOCK_SEQUENCE_01;
360     lock_sequence[2] = DAP_LOCK_SEQUENCE_23;
361     lock_sequence[3] = DAP_LOCK_SEQUENCE_23;
362 
363     // Initialize FLC
364     MXC_FLC_Init();
365 
366     // Unlock infoblock
367     MXC_FLC_UnlockInfoBlock(info_blk_addr);
368 
369     // Write DAP lock sequence to infoblock
370     err = MXC_FLC_Write128(info_blk_addr, lock_sequence);
371 
372     // Re-lock infoblock
373     MXC_FLC_LockInfoBlock(info_blk_addr);
374 
375     return err;
376 #endif
377 }
378 
379 /**@} end of mxc_sys */
380