1 /**
2  * @file    mxc_sys.h
3  * @brief   System level header file.
4  */
5 
6 /******************************************************************************
7  *
8  * Copyright (C) 2022-2023 Maxim Integrated Products, Inc. (now owned by
9  * Analog Devices, Inc.),
10  * Copyright (C) 2023-2024 Analog Devices, Inc.
11  *
12  * Licensed under the Apache License, Version 2.0 (the "License");
13  * you may not use this file except in compliance with the License.
14  * You may obtain a copy of the License at
15  *
16  *     http://www.apache.org/licenses/LICENSE-2.0
17  *
18  * Unless required by applicable law or agreed to in writing, software
19  * distributed under the License is distributed on an "AS IS" BASIS,
20  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21  * See the License for the specific language governing permissions and
22  * limitations under the License.
23  *
24  ******************************************************************************/
25 
26 #ifndef LIBRARIES_PERIPHDRIVERS_INCLUDE_MAX32660_MXC_SYS_H_
27 #define LIBRARIES_PERIPHDRIVERS_INCLUDE_MAX32660_MXC_SYS_H_
28 
29 #include "mxc_device.h"
30 #include "gcr_regs.h"
31 
32 #ifdef __cplusplus
33 extern "C" {
34 #endif
35 
36 /**
37  * @defgroup mxc_sys System Configuration (MXC_SYS)
38  * @ingroup syscfg
39  * @details API for system configuration including clock source selection and entering critical sections of code.
40  * @{
41  */
42 
43 /** @brief System reset0 and reset1 enumeration. Used in MXC_SYS_PeriphReset0 function */
44 typedef enum {
45     MXC_SYS_RESET0_DMA = MXC_F_GCR_RST0_DMA_POS, /**< Reset DMA */
46     MXC_SYS_RESET0_WDT0 = MXC_F_GCR_RST0_WDT0_POS, /**< Reset WDT */
47     MXC_SYS_RESET0_GPIO0 = MXC_F_GCR_RST0_GPIO0_POS, /**< Reset GPIO0 */
48     MXC_SYS_RESET0_TIMER0 = MXC_F_GCR_RST0_TIMER0_POS, /**< Reset TIMER0 */
49     MXC_SYS_RESET0_TIMER1 = MXC_F_GCR_RST0_TIMER1_POS, /**< Reset TIMER1 */
50     MXC_SYS_RESET0_TIMER2 = MXC_F_GCR_RST0_TIMER2_POS, /**< Reset TIMER2 */
51     MXC_SYS_RESET0_UART0 = MXC_F_GCR_RST0_UART0_POS, /**< Reset UART0 */
52     MXC_SYS_RESET0_UART1 = MXC_F_GCR_RST0_UART1_POS, /**< Reset UART1 */
53     MXC_SYS_RESET0_SPI0 = MXC_F_GCR_RST0_SPI0_POS, /**< Reset SPI0 */
54     MXC_SYS_RESET0_SPI1 = MXC_F_GCR_RST0_SPI1_POS, /**< Reset SPI1 */
55     MXC_SYS_RESET0_I2C0 = MXC_F_GCR_RST0_I2C0_POS, /**< Reset I2C0 */
56     MXC_SYS_RESET0_RTC = MXC_F_GCR_RST0_RTC_POS, /**< Reset RTC */
57     MXC_SYS_RESET0_SRST = MXC_F_GCR_RST0_SOFT_POS, /**< Soft reset */
58     MXC_SYS_RESET0_PRST = MXC_F_GCR_RST0_PERIPH_POS, /**< Peripheral reset */
59     MXC_SYS_RESET0_SYSTEM = MXC_F_GCR_RST0_SYSTEM_POS, /**< System reset */
60     /* RESET1 Below this line we add 32 to separate RESET0 and RESET1 */
61     MXC_SYS_RESET1_I2C1 = (MXC_F_GCR_RST1_I2C1_POS + 32), /**< Reset I2C1 */
62 } mxc_sys_reset_t;
63 
64 /** @brief System clock disable enumeration. Used in MXC_SYS_ClockDisable and MXC_SYS_ClockEnable functions */
65 typedef enum {
66     MXC_SYS_PERIPH_CLOCK_GPIO0 =
67         MXC_F_GCR_PCLK_DIS0_GPIO0D_POS, /**< Disable MXC_F_GCR_PCLKDIS0_GPIO0 clock */
68     MXC_SYS_PERIPH_CLOCK_DMA =
69         MXC_F_GCR_PCLK_DIS0_DMAD_POS, /**< Disable MXC_F_GCR_PCLKDIS0_DMA clock */
70     MXC_SYS_PERIPH_CLOCK_SPI0 =
71         MXC_F_GCR_PCLK_DIS0_SPI0D_POS, /**< Disable MXC_F_GCR_PCLKDIS0_SPI0 clock */
72     MXC_SYS_PERIPH_CLOCK_SPI1 =
73         MXC_F_GCR_PCLK_DIS0_SPI1D_POS, /**< Disable MXC_F_GCR_PCLKDIS0_SPI1 clock */
74     MXC_SYS_PERIPH_CLOCK_UART0 =
75         MXC_F_GCR_PCLK_DIS0_UART0D_POS, /**< Disable MXC_F_GCR_PCLKDIS0_UART0 clock */
76     MXC_SYS_PERIPH_CLOCK_UART1 =
77         MXC_F_GCR_PCLK_DIS0_UART1D_POS, /**< Disable MXC_F_GCR_PCLKDIS0_UART1 clock */
78     MXC_SYS_PERIPH_CLOCK_I2C0 =
79         MXC_F_GCR_PCLK_DIS0_I2C0D_POS, /**< Disable MXC_F_GCR_PCLKDIS0_I2C0 clock */
80     MXC_SYS_PERIPH_CLOCK_TMR0 =
81         MXC_F_GCR_PCLK_DIS0_TIMER0D_POS, /**< Disable MXC_F_GCR_PCLKDIS0_T0 clock */
82     MXC_SYS_PERIPH_CLOCK_TMR1 =
83         MXC_F_GCR_PCLK_DIS0_TIMER1D_POS, /**< Disable MXC_F_GCR_PCLKDIS0_T1 clock */
84     MXC_SYS_PERIPH_CLOCK_TMR2 =
85         MXC_F_GCR_PCLK_DIS0_TIMER2D_POS, /**< Disable MXC_F_GCR_PCLKDIS0_T2 clock */
86     MXC_SYS_PERIPH_CLOCK_I2C1 =
87         MXC_F_GCR_PCLK_DIS0_I2C1D_POS, /**< Disable MXC_F_GCR_PCLKDIS0_I2C1 clock */
88     /* PCLKDIS1 Below this line we add 32 to separate PCLKDIS0 and PCLKDIS1 */
89     MXC_SYS_PERIPH_CLOCK_FLCD =
90         (MXC_F_GCR_PCLK_DIS1_FLCD_POS + 32), /**< Disable MXC_F_GCR_PCLKDIS1_WDT1 clock */
91     MXC_SYS_PERIPH_CLOCK_ICACHE =
92         (MXC_F_GCR_PCLK_DIS1_ICCD_POS + 32), /**< Disable MXC_F_GCR_PCLKDIS1_I2S clock */
93 } mxc_sys_periph_clock_t;
94 
95 /** @brief Enumeration to select System Clock source */
96 typedef enum {
97     MXC_SYS_CLOCK_NANORING = MXC_V_GCR_CLK_CTRL_CLKSEL_NANORING, /**< 8KHz nanoring on MAX32660 */
98     MXC_SYS_CLOCK_HFXIN = MXC_V_GCR_CLK_CTRL_CLKSEL_HFXIN, /**< 32KHz on MAX32660 */
99     MXC_SYS_CLOCK_HFXIN_DIGITAL = 0x9, /**< External Clock Input*/
100     MXC_SYS_CLOCK_HIRC = MXC_V_GCR_CLK_CTRL_CLKSEL_HIRC, /**< High Frequency Internal Oscillator */
101 } mxc_sys_system_clock_t;
102 
103 typedef enum {
104     MXC_SYS_CLOCK_DIV_1 = MXC_S_GCR_CLK_CTRL_PSC_DIV1,
105     MXC_SYS_CLOCK_DIV_2 = MXC_S_GCR_CLK_CTRL_PSC_DIV2,
106     MXC_SYS_CLOCK_DIV_4 = MXC_S_GCR_CLK_CTRL_PSC_DIV4,
107     MXC_SYS_CLOCK_DIV_8 = MXC_S_GCR_CLK_CTRL_PSC_DIV8,
108     MXC_SYS_CLOCK_DIV_16 = MXC_S_GCR_CLK_CTRL_PSC_DIV16,
109     MXC_SYS_CLOCK_DIV_32 = MXC_S_GCR_CLK_CTRL_PSC_DIV32,
110     MXC_SYS_CLOCK_DIV_64 = MXC_S_GCR_CLK_CTRL_PSC_DIV64,
111     MXC_SYS_CLOCK_DIV_128 = MXC_S_GCR_CLK_CTRL_PSC_DIV128
112 } mxc_sys_system_clock_div_t;
113 
114 #define MXC_SYS_USN_LEN 8
115 
116 /***** Function Prototypes *****/
117 
118 typedef struct {
119     int ie_status;
120     int in_critical;
121 } mxc_crit_state_t;
122 
123 static mxc_crit_state_t _state = { .ie_status = (int)0xFFFFFFFF, .in_critical = 0 };
124 
_mxc_crit_get_state(void)125 static inline void _mxc_crit_get_state(void)
126 {
127 #ifndef __riscv
128     /*
129         On ARM M the 0th bit of the Priority Mask register indicates
130         whether interrupts are enabled or not.
131 
132         0 = enabled
133         1 = disabled
134     */
135     uint32_t primask = __get_PRIMASK();
136     _state.ie_status = (primask == 0);
137 #else
138     /*
139         On RISC-V bit position 3 (Machine Interrupt Enable) of the
140         mstatus register indicates whether interrupts are enabled.
141 
142         0 = disabled
143         1 = enabled
144     */
145     uint32_t mstatus = get_mstatus();
146     _state.ie_status = ((mstatus & (1 << 3)) != 0);
147 #endif
148 }
149 
150 /**
151  * @brief Enter a critical section of code that cannot be interrupted.  Call @ref MXC_SYS_Crit_Exit to exit the critical section.
152  * @details Ex:
153  * @code
154  * MXC_SYS_Crit_Enter();
155  * printf("Hello critical section!\n");
156  * MXC_SYS_Crit_Exit();
157  * @endcode
158  * The @ref MXC_CRITICAL macro is also provided as a convencience macro for wrapping a code section in this way.
159  * @returns None
160  */
MXC_SYS_Crit_Enter(void)161 static inline void MXC_SYS_Crit_Enter(void)
162 {
163     _mxc_crit_get_state();
164     if (_state.ie_status)
165         __disable_irq();
166     _state.in_critical = 1;
167 }
168 
169 /**
170  * @brief Exit a critical section of code from @ref MXC_SYS_Crit_Enter
171  * @returns None
172  */
MXC_SYS_Crit_Exit(void)173 static inline void MXC_SYS_Crit_Exit(void)
174 {
175     if (_state.ie_status) {
176         __enable_irq();
177     }
178     _state.in_critical = 0;
179     _mxc_crit_get_state();
180     /*
181         ^ Reset the state again to prevent edge case
182         where interrupts get disabled, then Crit_Exit() gets
183         called, which would inadvertently re-enable interrupts
184         from old state.
185     */
186 }
187 
188 /**
189  * @brief Polls whether code is currently executing from a critical section.
190  * @returns 1 if code is currently in a critical section (interrupts are disabled).
191  *          0 if code is not in a critical section.
192  */
MXC_SYS_In_Crit_Section(void)193 static inline int MXC_SYS_In_Crit_Section(void)
194 {
195     return _state.in_critical;
196 }
197 
198 // clang-format off
199 /**
200  * @brief Macro for wrapping a section of code to make it critical (interrupts disabled).  Note: this macro
201  * does not support nesting.
202  * @details
203  * Ex:
204  * \code
205  * MXC_CRITICAL(
206  *      printf("Hello critical section!\n");
207  * )
208  * \endcode
209  * This macro places a call to @ref MXC_SYS_Crit_Enter before the code, and a call to @ref MXC_SYS_Crit_Exit after.
210  * @param code The code section to wrap.
211  */
212 #define MXC_CRITICAL(code) {\
213     MXC_SYS_Crit_Enter();\
214     code;\
215     MXC_SYS_Crit_Exit();\
216 }
217 // clang-format on
218 
219 /**
220  * @brief Reads the device USN.
221  * @param usn     Pointer to store the USN.
222  * @param len     Length of the USN buffer
223  * @param part    Which USN part you want (0, 1, 2)
224  * @returns       E_NO_ERROR if everything is successful.
225  */
226 int MXC_SYS_GetUSN(uint8_t *usn, int len, int part);
227 
228 /**
229  * @brief Determines if the selected peripheral clock is enabled.
230  * @param clock   Enumeration for desired clock.
231  * @returns       0 is the clock is disabled, non 0 if the clock is enabled.
232  */
233 int MXC_SYS_IsClockEnabled(mxc_sys_periph_clock_t clock);
234 
235 /**
236  * @brief Disables the selected peripheral clock.
237  * @param clock   Enumeration for desired clock.
238  */
239 void MXC_SYS_ClockDisable(mxc_sys_periph_clock_t clock);
240 
241 /**
242  * @brief Enables the selected peripheral clock.
243  * @param clock   Enumeration for desired clock.
244  */
245 void MXC_SYS_ClockEnable(mxc_sys_periph_clock_t clock);
246 
247 /**
248  * @brief Enables the 32kHz oscillator
249  * @param mxc_sys_cfg   Not used, may be NULL.
250  */
251 void MXC_SYS_RTCClockEnable(void);
252 
253 /**
254  * @brief Disables the 32kHz oscillator
255  * @returns         E_NO_ERROR if everything is successful
256  */
257 int MXC_SYS_RTCClockDisable(void);
258 
259 /**
260  * @brief Enable System Clock Source without switching to it
261  * @param      clock The clock to enable
262  * @return     E_NO_ERROR if everything is successful
263  */
264 int MXC_SYS_ClockSourceEnable(mxc_sys_system_clock_t clock);
265 
266 /**
267  * @brief Disable System Clock Source
268  * @param      clock The clock to disable
269  * @return     E_NO_ERROR if everything is successful
270  */
271 int MXC_SYS_ClockSourceDisable(mxc_sys_system_clock_t clock);
272 
273 /**
274  * @brief Select the system clock.
275  * @param clock     Enumeration for desired clock.
276  * @returns         E_NO_ERROR if everything is successful.
277  */
278 int MXC_SYS_Clock_Select(mxc_sys_system_clock_t clock);
279 
280 /**
281  * @brief Set the system clock divider.
282  * @param div       Enumeration for desired clock divider.
283  */
284 void MXC_SYS_SetClockDiv(mxc_sys_system_clock_div_t div);
285 
286 /**
287  * @brief Get the system clock divider.
288  * @returns         System clock divider.
289  */
290 mxc_sys_system_clock_div_t MXC_SYS_GetClockDiv(void);
291 
292 /**
293  * @brief Wait for a clock to enable with timeout
294  * @param      ready The clock to wait for
295  * @return     E_NO_ERROR if ready, E_TIME_OUT if timeout
296  */
297 int MXC_SYS_Clock_Timeout(uint32_t ready);
298 
299 /**
300  * @brief Reset the peripherals and/or CPU in the rstr0 or rstr1 register.
301  * @param           Enumeration for what to reset. Can reset multiple items at once.
302  */
303 void MXC_SYS_Reset_Periph(mxc_sys_reset_t reset);
304 
305 /**
306  * @brief This function PERMANENTLY locks the Debug Access Port.
307  *
308  * @warning After executing this function you will never be able
309  *          to reprogram the target micro.
310  */
311 int MXC_SYS_LockDAP_Permanent(void);
312 
313 #ifdef __cplusplus
314 }
315 #endif
316 
317 #endif // LIBRARIES_PERIPHDRIVERS_INCLUDE_MAX32660_MXC_SYS_H_
318