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