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.h
23  * @brief   System level header file.
24  */
25 
26 #ifndef LIBRARIES_PERIPHDRIVERS_INCLUDE_MAX32662_MXC_SYS_H_
27 #define LIBRARIES_PERIPHDRIVERS_INCLUDE_MAX32662_MXC_SYS_H_
28 
29 #include "mxc_device.h"
30 #include "gcr_regs.h"
31 #include "mcr_regs.h"
32 
33 #ifdef __cplusplus
34 extern "C" {
35 #endif
36 
37 /**
38  * @defgroup mxc_sys System Configuration (MXC_SYS)
39  * @ingroup syscfg
40  * @details API for system configuration including clock source selection and entering critical sections of code.
41  * @{
42  */
43 
44 /** @brief System reset0 and reset1 enumeration. Used in MXC_SYS_PeriphReset0 function */
45 typedef enum {
46     MXC_SYS_RESET0_DMA = MXC_F_GCR_RST0_DMA_POS, /**< Reset DMA */
47     MXC_SYS_RESET0_WDT = MXC_F_GCR_RST0_WDT_POS, /**< Reset WDT */
48     MXC_SYS_RESET0_GPIO0 = MXC_F_GCR_RST0_GPIO0_POS, /**< Reset GPIO0 */
49     MXC_SYS_RESET0_TMR0 = MXC_F_GCR_RST0_TMR0_POS, /**< Reset TMR0 */
50     MXC_SYS_RESET0_TMR1 = MXC_F_GCR_RST0_TMR1_POS, /**< Reset TMR1 */
51     MXC_SYS_RESET0_TMR2 = MXC_F_GCR_RST0_TMR2_POS, /**< Reset TMR2 */
52     MXC_SYS_RESET0_UART0 = MXC_F_GCR_RST0_UART0_POS, /**< Reset UART0 */
53     MXC_SYS_RESET0_UART1 = MXC_F_GCR_RST0_UART1_POS, /**< Reset UART1 */
54     MXC_SYS_RESET0_SPI0 = MXC_F_GCR_RST0_SPI0_POS, /**< Reset SPI0 */
55     MXC_SYS_RESET0_SPI1 = MXC_F_GCR_RST0_SPI1_POS, /**< Reset SPI1 */
56     MXC_SYS_RESET0_I2C0 = MXC_F_GCR_RST0_I2C0_POS, /**< Reset I2C0 */
57     MXC_SYS_RESET0_CAN = MXC_F_GCR_RST0_CAN_POS, /**< Reset CAN */
58     MXC_SYS_RESET0_TRNG = MXC_F_GCR_RST0_TRNG_POS, /**< Reset TRNG */
59     MXC_SYS_RESET0_ADC = MXC_F_GCR_RST0_ADC_POS, /**< Reset ADC */
60     MXC_SYS_RESET0_SOFT = MXC_F_GCR_RST0_SOFT_POS, /**< Soft reset */
61     MXC_SYS_RESET0_PERIPH = MXC_F_GCR_RST0_PERIPH_POS, /**< Peripheral reset */
62     MXC_SYS_RESET0_SYS = MXC_F_GCR_RST0_SYS_POS, /**< System reset */
63     /* RESET1 Below this line we add 32 to separate RESET0 and RESET1 */
64     MXC_SYS_RESET1_I2C1 = (MXC_F_GCR_RST1_I2C1_POS + 32), /**< Reset I2C1 */
65     MXC_SYS_RESET1_PT = (MXC_F_GCR_RST1_PT_POS + 32), /**< Reset PT */
66     MXC_SYS_RESET1_AES = (MXC_F_GCR_RST1_AES_POS + 32), /**< Reset AES */
67     MXC_SYS_RESET1_AC = (MXC_F_GCR_RST1_AC_POS + 32), /**< Reset Auto-Cal */
68     MXC_SYS_RESET1_I2S = (MXC_F_GCR_RST1_I2S_POS + 32), /**< Reset I2S */
69     /* LP Peripheral Reset below this line. Adding 64 to separate from non-LP periphs. */
70     MXC_SYS_RESET_TMR3 = (MXC_F_MCR_RST_TMR3_POS + 64), /**< Reset TMR3 */
71     MXC_SYS_RESET_RTC = (MXC_F_MCR_RST_RTC_POS + 64) /**< Reset RTC */
72 } mxc_sys_reset_t;
73 
74 /** @brief System clock disable enumeration. Used in MXC_SYS_ClockDisable and MXC_SYS_ClockEnable functions */
75 typedef enum {
76     MXC_SYS_PERIPH_CLOCK_GPIO0 =
77         MXC_F_GCR_PCLKDIS0_GPIO0_POS, /**< Disable MXC_F_GCR_PCLKDIS0_GPIO0 clock */
78     MXC_SYS_PERIPH_CLOCK_DMA =
79         MXC_F_GCR_PCLKDIS0_DMA_POS, /**< Disable MXC_F_GCR_PCLKDIS0_DMA clock */
80     MXC_SYS_PERIPH_CLOCK_SPI0 =
81         MXC_F_GCR_PCLKDIS0_SPI0_POS, /**< Disable MXC_F_GCR_PCLKDIS0_SPI0 clock */
82     MXC_SYS_PERIPH_CLOCK_SPI1 =
83         MXC_F_GCR_PCLKDIS0_SPI1_POS, /**< Disable MXC_F_GCR_PCLKDIS0_SPI1 clock */
84     MXC_SYS_PERIPH_CLOCK_UART0 =
85         MXC_F_GCR_PCLKDIS0_UART0_POS, /**< Disable MXC_F_GCR_PCLKDIS0_UART0 clock */
86     MXC_SYS_PERIPH_CLOCK_UART1 =
87         MXC_F_GCR_PCLKDIS0_UART1_POS, /**< Disable MXC_F_GCR_PCLKDIS0_UART1 clock */
88     MXC_SYS_PERIPH_CLOCK_I2C0 =
89         MXC_F_GCR_PCLKDIS0_I2C0_POS, /**< Disable MXC_F_GCR_PCLKDIS0_I2C0 clock */
90     MXC_SYS_PERIPH_CLOCK_TMR0 =
91         MXC_F_GCR_PCLKDIS0_TMR0_POS, /**< Disable MXC_F_GCR_PCLKDIS0_TMR0 clock */
92     MXC_SYS_PERIPH_CLOCK_TMR1 =
93         MXC_F_GCR_PCLKDIS0_TMR1_POS, /**< Disable MXC_F_GCR_PCLKDIS0_TMR1 clock */
94     MXC_SYS_PERIPH_CLOCK_TMR2 =
95         MXC_F_GCR_PCLKDIS0_TMR2_POS, /**< Disable MXC_F_GCR_PCLKDIS0_TMR2 clock */
96     MXC_SYS_PERIPH_CLOCK_ADC =
97         MXC_F_GCR_PCLKDIS0_ADC_POS, /**< Disable MXC_F_GCR_PCLKDIS0_ADC clock */
98     MXC_SYS_PERIPH_CLOCK_I2C1 =
99         MXC_F_GCR_PCLKDIS0_I2C1_POS, /**< Disable MXC_F_GCR_PCLKDIS0_I2C1 clock */
100     MXC_SYS_PERIPH_CLOCK_PT = MXC_F_GCR_PCLKDIS0_PT_POS, /**< Disable MXC_F_GCR_PCLKDIS0_PT clock */
101     /* PCLKDIS1 below this line we add 32 to separate PCLKDIS0 and PCLKDIS1 */
102     MXC_SYS_PERIPH_CLOCK_TRNG =
103         (MXC_F_GCR_PCLKDIS1_TRNG_POS + 32), /**< Disable MXC_F_GCR_PCLKDIS1_TRNG clock */
104     MXC_SYS_PERIPH_CLOCK_WDT =
105         (MXC_F_GCR_PCLKDIS1_WDT_POS + 32), /**< Disable MXC_F_GCR_PCLKDIS1_WDT clock */
106     MXC_SYS_PERIPH_CLOCK_CAN =
107         (MXC_F_GCR_PCLKDIS1_CAN_POS + 32), /**< Disable MXC_F_GCR_PCLKDIS1_CAM clock */
108     MXC_SYS_PERIPH_CLOCK_AES =
109         (MXC_F_GCR_PCLKDIS1_AES_POS + 32), /**< Disable MXC_F_GCR_PCLKDIS1_AES clock */
110     MXC_SYS_PERIPH_CLOCK_I2S =
111         (MXC_F_GCR_PCLKDIS1_I2S_POS + 32), /**< Disable MXC_F_GCR_PCLKDIS1_I2S clock */
112     /* MCR_PCLKDIS below this line we add 64 to separate from GCR_PCLKDIS0 and GCR_PCLKDIS1 */
113     MXC_SYS_PERIPH_CLOCK_TMR3 =
114         (MXC_F_MCR_PCLKDIS_TMR3_POS + 64), /**< Disable MXC_F_GCR_PCLKDIS1_I2S clock */
115 } mxc_sys_periph_clock_t;
116 
117 /** @brief Enumeration to select System Clock source */
118 typedef enum {
119     MXC_SYS_CLOCK_IPO =
120         MXC_V_GCR_CLKCTRL_SYSCLK_SEL_IPO, /**< Select the Internal Primary Oscillator (IPO) */
121     MXC_SYS_CLOCK_ERFO =
122         MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ERFO, /**< Select the External RF Crystal Oscillator */
123     MXC_SYS_CLOCK_IBRO =
124         MXC_V_GCR_CLKCTRL_SYSCLK_SEL_IBRO, /**< Select the Internal Baud Rate Oscillator (IBRO) */
125     MXC_SYS_CLOCK_INRO =
126         MXC_V_GCR_CLKCTRL_SYSCLK_SEL_INRO, /**< Select the External RF Crystal Oscillator */
127     MXC_SYS_CLOCK_ERTCO =
128         MXC_V_GCR_CLKCTRL_SYSCLK_SEL_ERTCO, /**< Select the External RTC Crystal Oscillator */
129     MXC_SYS_CLOCK_EXTCLK =
130         MXC_V_GCR_CLKCTRL_SYSCLK_SEL_EXTCLK /**< Use the external system clock input */
131 } mxc_sys_system_clock_t;
132 
133 typedef enum {
134     MXC_SYS_CLOCK_DIV_1 = MXC_S_GCR_CLKCTRL_SYSCLK_DIV_DIV1,
135     MXC_SYS_CLOCK_DIV_2 = MXC_S_GCR_CLKCTRL_SYSCLK_DIV_DIV2,
136     MXC_SYS_CLOCK_DIV_4 = MXC_S_GCR_CLKCTRL_SYSCLK_DIV_DIV4,
137     MXC_SYS_CLOCK_DIV_8 = MXC_S_GCR_CLKCTRL_SYSCLK_DIV_DIV8,
138     MXC_SYS_CLOCK_DIV_16 = MXC_S_GCR_CLKCTRL_SYSCLK_DIV_DIV16,
139     MXC_SYS_CLOCK_DIV_32 = MXC_S_GCR_CLKCTRL_SYSCLK_DIV_DIV32,
140     MXC_SYS_CLOCK_DIV_64 = MXC_S_GCR_CLKCTRL_SYSCLK_DIV_DIV64,
141     MXC_SYS_CLOCK_DIV_128 = MXC_S_GCR_CLKCTRL_SYSCLK_DIV_DIV128
142 } mxc_sys_system_clock_div_t;
143 
144 #define MXC_SYS_USN_CHECKSUM_LEN 16 // Length of the USN + padding for checksum compute
145 #define MXC_SYS_USN_CSUM_FIELD_LEN 2 // Size of the checksum field in the USN
146 #define MXC_SYS_USN_LEN 13 // Size of the USN including the checksum
147 
148 /***** Function Prototypes *****/
149 
150 typedef struct {
151     int ie_status;
152     int in_critical;
153 } mxc_crit_state_t;
154 
155 static mxc_crit_state_t _state = { .ie_status = (int)0xFFFFFFFF, .in_critical = 0 };
156 
_mxc_crit_get_state(void)157 static inline void _mxc_crit_get_state(void)
158 {
159 #ifndef __riscv
160     /*
161         On ARM M the 0th bit of the Priority Mask register indicates
162         whether interrupts are enabled or not.
163 
164         0 = enabled
165         1 = disabled
166     */
167     uint32_t primask = __get_PRIMASK();
168     _state.ie_status = (primask == 0);
169 #else
170     /*
171         On RISC-V bit position 3 (Machine Interrupt Enable) of the
172         mstatus register indicates whether interrupts are enabled.
173 
174         0 = disabled
175         1 = enabled
176     */
177     uint32_t mstatus = get_mstatus();
178     _state.ie_status = ((mstatus & (1 << 3)) != 0);
179 #endif
180 }
181 
182 /**
183  * @brief Enter a critical section of code that cannot be interrupted.  Call @ref MXC_SYS_Crit_Exit to exit the critical section.
184  * @details Ex:
185  * @code
186  * MXC_SYS_Crit_Enter();
187  * printf("Hello critical section!\n");
188  * MXC_SYS_Crit_Exit();
189  * @endcode
190  * The @ref MXC_CRITICAL macro is also provided as a convencience macro for wrapping a code section in this way.
191  * @returns None
192  */
MXC_SYS_Crit_Enter(void)193 static inline void MXC_SYS_Crit_Enter(void)
194 {
195     _mxc_crit_get_state();
196     if (_state.ie_status)
197         __disable_irq();
198     _state.in_critical = 1;
199 }
200 
201 /**
202  * @brief Exit a critical section of code from @ref MXC_SYS_Crit_Enter
203  * @returns None
204  */
MXC_SYS_Crit_Exit(void)205 static inline void MXC_SYS_Crit_Exit(void)
206 {
207     if (_state.ie_status) {
208         __enable_irq();
209     }
210     _state.in_critical = 0;
211     _mxc_crit_get_state();
212     /*
213         ^ Reset the state again to prevent edge case
214         where interrupts get disabled, then Crit_Exit() gets
215         called, which would inadvertently re-enable interrupts
216         from old state.
217     */
218 }
219 
220 /**
221  * @brief Polls whether code is currently executing from a critical section.
222  * @returns 1 if code is currently in a critical section (interrupts are disabled).
223  *          0 if code is not in a critical section.
224  */
MXC_SYS_In_Crit_Section(void)225 static inline int MXC_SYS_In_Crit_Section(void)
226 {
227     return _state.in_critical;
228 }
229 
230 // clang-format off
231 /**
232  * @brief Macro for wrapping a section of code to make it critical (interrupts disabled).  Note: this macro
233  * does not support nesting.
234  * @details
235  * Ex:
236  * \code
237  * MXC_CRITICAL(
238  *      printf("Hello critical section!\n");
239  * )
240  * \endcode
241  * This macro places a call to @ref MXC_SYS_Crit_Enter before the code, and a call to @ref MXC_SYS_Crit_Exit after.
242  * @param code The code section to wrap.
243  */
244 #define MXC_CRITICAL(code) {\
245     MXC_SYS_Crit_Enter();\
246     code;\
247     MXC_SYS_Crit_Exit();\
248 }
249 // clang-format on
250 
251 /**
252  * @brief Reads the device USN and verifies the checksum.
253  * @param usn       Pointer to store the USN. Array must be at least MXC_SYS_USN_LEN bytes long.
254  * @param checksum  Optional pointer to store the AES checksum. If not NULL, checksum is verified with AES engine.
255  * @returns         E_NO_ERROR if everything is successful.
256  */
257 int MXC_SYS_GetUSN(uint8_t *usn, uint8_t *checksum);
258 
259 /**
260  * @brief Determines if the selected peripheral clock is enabled.
261  * @param clock   Enumeration for desired clock.
262  * @returns       0 is the clock is disabled, non 0 if the clock is enabled.
263  */
264 int MXC_SYS_IsClockEnabled(mxc_sys_periph_clock_t clock);
265 
266 /**
267  * @brief Disables the selected peripheral clock.
268  * @param clock   Enumeration for desired clock.
269  */
270 void MXC_SYS_ClockDisable(mxc_sys_periph_clock_t clock);
271 
272 /**
273  * @brief Enables the selected peripheral clock.
274  * @param clock   Enumeration for desired clock.
275  */
276 void MXC_SYS_ClockEnable(mxc_sys_periph_clock_t clock);
277 
278 /**
279  * @brief Enables the 32kHz oscillator
280  * @param mxc_sys_cfg   Not used, may be NULL.
281  */
282 void MXC_SYS_RTCClockEnable(void);
283 
284 /**
285  * @brief Disables the 32kHz oscillator
286  * @returns         E_NO_ERROR if everything is successful
287  */
288 int MXC_SYS_RTCClockDisable(void);
289 
290 /**
291  * @brief Enable System Clock Source without switching to it
292  * @param      clock The clock to enable
293  * @return     E_NO_ERROR if everything is successful
294  */
295 int MXC_SYS_ClockSourceEnable(mxc_sys_system_clock_t clock);
296 
297 /**
298  * @brief Disable System Clock Source
299  * @param      clock The clock to disable
300  * @return     E_NO_ERROR if everything is successful
301  */
302 int MXC_SYS_ClockSourceDisable(mxc_sys_system_clock_t clock);
303 
304 /**
305  * @brief Select the system clock.
306  * @param clock     Enumeration for desired clock.  Note:  If using the external clock input be sure to define EXTCLK_FREQ correctly.
307  *                  The default EXTCLK_FREQ value is defined in the system_max32662.h file and can be overridden at compile time.
308  * @returns         E_NO_ERROR if everything is successful.
309  */
310 int MXC_SYS_Clock_Select(mxc_sys_system_clock_t clock);
311 
312 /**
313  * @brief Set the system clock divider.
314  * @param div       Enumeration for desired clock divider.
315  */
316 void MXC_SYS_SetClockDiv(mxc_sys_system_clock_div_t div);
317 
318 /**
319  * @brief Get the system clock divider.
320  * @returns         System clock divider.
321  */
322 mxc_sys_system_clock_div_t MXC_SYS_GetClockDiv(void);
323 
324 /**
325  * @brief Wait for a clock to enable with timeout
326  * @param      ready The clock to wait for
327  * @return     E_NO_ERROR if ready, E_TIME_OUT if timeout
328  */
329 int MXC_SYS_Clock_Timeout(uint32_t ready);
330 
331 /**
332  * @brief Reset the peripherals and/or CPU in the rstr0 or rstr1 register.
333  * @param           Enumeration for what to reset. Can reset multiple items at once.
334  */
335 void MXC_SYS_Reset_Periph(mxc_sys_reset_t reset);
336 
337 /**
338  * @brief This function PERMANENTLY locks the Debug Access Port.
339  *
340  * @warning After executing this function you will never be able
341  *          to reprogram the target micro.
342  */
343 int MXC_SYS_LockDAP_Permanent(void);
344 
345 #ifdef __cplusplus
346 }
347 #endif
348 
349 #endif // LIBRARIES_PERIPHDRIVERS_INCLUDE_MAX32662_MXC_SYS_H_
350