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