1 /*
2  * Copyright (c) 2017 Google LLC.
3  * Copyright (c) 2023 Ionut Catalin Pavel <iocapa@iocapa.com>
4  * Copyright (c) 2023-2025 Gerson Fernando Budke <nandojve@gmail.com>
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 /**
10  * @file
11  * @brief Atmel SAMD MCU series initialization code
12  */
13 
14 /* The CPU clock will be configured to the DT requested value,
15  * and run via DFLL48M.
16  *
17  * Reference -> GCLK Gen 1 -> DFLL48M -> GCLK Gen 0 -> GCLK_MAIN
18  *
19  * GCLK Gen 0 -> GCLK_MAIN
20  * GCLK Gen 1 -> DFLL48M (variable)
21  * GCLK Gen 2 -> WDT @ 32768 Hz
22  * GCLK Gen 3 -> ADC @ 8 MHz
23  * GCLK Gen 4 -> RTC @ xtal 32768 Hz
24  */
25 
26 #include <zephyr/device.h>
27 #include <zephyr/init.h>
28 #include <zephyr/kernel.h>
29 
30 #include <soc.h>
31 #include <cmsis_core.h>
32 
33 /* clang-format off */
34 
35 /**
36  * Fix different naming conventions for SAMD20
37  */
38 #ifdef FUSES_OSC32KCAL_ADDR
39 #define FUSES_OSC32K_CAL_ADDR		FUSES_OSC32KCAL_ADDR
40 #define FUSES_OSC32K_CAL_Pos		FUSES_OSC32KCAL_Pos
41 #define FUSES_OSC32K_CAL_Msk		FUSES_OSC32KCAL_Msk
42 #endif
43 
gclk_connect(uint8_t gclk,uint32_t src,uint32_t div,uint32_t flags)44 static void gclk_connect(uint8_t gclk, uint32_t src, uint32_t div, uint32_t flags)
45 {
46 	GCLK->GENDIV.reg = GCLK_GENDIV_ID(gclk)
47 			 | GCLK_GENDIV_DIV(div);
48 
49 	while (GCLK->STATUS.bit.SYNCBUSY) {
50 	}
51 
52 	GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(gclk)
53 			  | GCLK_GENCTRL_GENEN
54 			  | src
55 			  | flags;
56 
57 	while (GCLK->STATUS.bit.SYNCBUSY) {
58 	}
59 }
60 
osc8m_init(void)61 static inline void osc8m_init(void)
62 {
63 	uint32_t reg;
64 
65 	/* Save calibration */
66 	reg = SYSCTRL->OSC8M.reg
67 	    & (SYSCTRL_OSC8M_FRANGE_Msk | SYSCTRL_OSC8M_CALIB_Msk);
68 
69 	SYSCTRL->OSC8M.reg = reg
70 			   | SYSCTRL_OSC8M_RUNSTDBY
71 			   | SYSCTRL_OSC8M_PRESC(0) /* 8MHz (/1) */
72 			   | SYSCTRL_OSC8M_ENABLE;
73 
74 	while (!SYSCTRL->PCLKSR.bit.OSC8MRDY) {
75 	}
76 
77 	/* Use 8Mhz clock as gclk_main to allow switching between clocks
78 	 * when using bootloaders
79 	 */
80 	gclk_connect(0, GCLK_GENCTRL_SRC_OSC8M, 0, 0);
81 }
82 
83 #if !CONFIG_SOC_ATMEL_SAMD_OSC32K || CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN
84 #define osc32k_init()
85 #else
osc32k_init(void)86 static inline void osc32k_init(void)
87 {
88 	uint32_t cal;
89 
90 	/* Get calibration value */
91 	cal = (*((uint32_t *)FUSES_OSC32K_CAL_ADDR)
92 	    & FUSES_OSC32K_CAL_Msk) >> FUSES_OSC32K_CAL_Pos;
93 
94 	SYSCTRL->OSC32K.reg = SYSCTRL_OSC32K_CALIB(cal)
95 			    | SYSCTRL_OSC32K_STARTUP(0x5) /* 34 cycles / ~1ms */
96 			    | SYSCTRL_OSC32K_RUNSTDBY
97 			    | SYSCTRL_OSC32K_EN32K
98 			    | SYSCTRL_OSC32K_ENABLE;
99 
100 	while (!SYSCTRL->PCLKSR.bit.OSC32KRDY) {
101 	}
102 }
103 #endif
104 
105 #if !CONFIG_SOC_ATMEL_SAMD_XOSC || CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN
106 #define xosc_init()
107 #else
xosc_init(void)108 static inline void xosc_init(void)
109 {
110 	SYSCTRL->XOSC.reg = SYSCTRL_XOSC_STARTUP(0x5) /* 32 cycles / ~1ms */
111 			  | SYSCTRL_XOSC_RUNSTDBY
112 			  | SYSCTRL_XOSC_AMPGC
113 #if CONFIG_SOC_ATMEL_SAMD_XOSC_FREQ_HZ <= 2000000
114 			  | SYSCTRL_XOSC_GAIN(0x0)
115 #elif CONFIG_SOC_ATMEL_SAMD_XOSC_FREQ_HZ <= 4000000
116 			  | SYSCTRL_XOSC_GAIN(0x1)
117 #elif CONFIG_SOC_ATMEL_SAMD_XOSC_FREQ_HZ <= 8000000
118 			  | SYSCTRL_XOSC_GAIN(0x2)
119 #elif CONFIG_SOC_ATMEL_SAMD_XOSC_FREQ_HZ <= 16000000
120 			  | SYSCTRL_XOSC_GAIN(0x3)
121 #elif CONFIG_SOC_ATMEL_SAMD_XOSC_FREQ_HZ <= 32000000
122 			  | SYSCTRL_XOSC_GAIN(0x4)
123 #endif
124 #if CONFIG_SOC_ATMEL_SAMD_XOSC_CRYSTAL
125 			  | SYSCTRL_XOSC_XTALEN
126 #endif
127 			  | SYSCTRL_XOSC_ENABLE;
128 
129 	while (!SYSCTRL->PCLKSR.bit.XOSCRDY) {
130 	}
131 }
132 #endif
133 
134 #if !CONFIG_SOC_ATMEL_SAMD_XOSC32K || CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN
135 #define xosc32k_init()
136 #else
xosc32k_init(void)137 static inline void xosc32k_init(void)
138 {
139 	SYSCTRL->XOSC32K.reg = SYSCTRL_XOSC32K_STARTUP(0x1) /* 4096 cycles / ~0.13s */
140 			     | SYSCTRL_XOSC32K_RUNSTDBY
141 			     | SYSCTRL_XOSC32K_EN32K
142 			     | SYSCTRL_XOSC32K_AAMPEN
143 #if CONFIG_SOC_ATMEL_SAMD_XOSC32K_CRYSTAL
144 			     | SYSCTRL_XOSC32K_XTALEN
145 #endif
146 			     | SYSCTRL_XOSC32K_ENABLE;
147 
148 	while (!SYSCTRL->PCLKSR.bit.XOSC32KRDY) {
149 	}
150 }
151 #endif
152 
153 #if CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN
154 #define dfll48m_init()
155 #else
dfll48m_init(void)156 static inline void dfll48m_init(void)
157 {
158 	uint32_t fcal, ccal;
159 
160 	gclk_connect(1,
161 #if CONFIG_SOC_ATMEL_SAMD_OSC32K_AS_MAIN
162 			  GCLK_GENCTRL_SRC_OSC32K,
163 #elif CONFIG_SOC_ATMEL_SAMD_XOSC32K_AS_MAIN
164 			  GCLK_GENCTRL_SRC_XOSC32K,
165 #elif CONFIG_SOC_ATMEL_SAMD_OSC8M_AS_MAIN
166 			  GCLK_GENCTRL_SRC_OSC8M,
167 #elif CONFIG_SOC_ATMEL_SAMD_XOSC_AS_MAIN
168 			  GCLK_GENCTRL_SRC_XOSC,
169 #endif
170 		SOC_ATMEL_SAM0_GCLK1_DIV, GCLK_GENCTRL_RUNSTDBY);
171 
172 	/* Route multiplexer 0 to DFLL48M */
173 	GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(0)
174 			  | GCLK_CLKCTRL_GEN_GCLK1
175 			  | GCLK_CLKCTRL_CLKEN;
176 
177 
178 	SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_MODE
179 			      | SYSCTRL_DFLLCTRL_QLDIS
180 			      | SYSCTRL_DFLLCTRL_RUNSTDBY;
181 
182 	/* Get calibration values */
183 	ccal = (*((uint32_t *)FUSES_DFLL48M_COARSE_CAL_ADDR)
184 	     & FUSES_DFLL48M_COARSE_CAL_Msk) >> FUSES_DFLL48M_COARSE_CAL_Pos;
185 
186 	fcal = (*((uint32_t *)FUSES_DFLL48M_FINE_CAL_ADDR)
187 	     & FUSES_DFLL48M_FINE_CAL_Msk) >> FUSES_DFLL48M_FINE_CAL_Pos;
188 
189 	SYSCTRL->DFLLVAL.reg = SYSCTRL_DFLLVAL_COARSE(ccal)
190 			     | SYSCTRL_DFLLVAL_FINE(fcal);
191 
192 	/* Use half of maximum for both */
193 	SYSCTRL->DFLLMUL.reg = SYSCTRL_DFLLMUL_CSTEP(31)
194 			     | SYSCTRL_DFLLMUL_FSTEP(511)
195 			     | SYSCTRL_DFLLMUL_MUL(SOC_ATMEL_SAM0_DFLL48M_MUL);
196 
197 	/* Enable */
198 	while (!SYSCTRL->PCLKSR.bit.DFLLRDY) {
199 	}
200 	SYSCTRL->DFLLCTRL.bit.ENABLE = 1;
201 
202 	/* Wait for synchronization. */
203 	while (!SYSCTRL->PCLKSR.bit.DFLLLCKC || !SYSCTRL->PCLKSR.bit.DFLLLCKF) {
204 	}
205 }
206 #endif
207 
208 #if CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN
209 #define flash_waitstates_init()
210 #else
flash_waitstates_init(void)211 static inline void flash_waitstates_init(void)
212 {
213 	NVMCTRL->CTRLB.bit.RWS = NVMCTRL_CTRLB_RWS(CONFIG_SOC_ATMEL_SAMD_NVM_WAIT_STATES);
214 }
215 #endif
216 
217 #if CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN
218 #define gclk_main_configure()
219 #else
gclk_main_configure(void)220 static inline void gclk_main_configure(void)
221 {
222 	gclk_connect(0, GCLK_GENCTRL_SRC_DFLL48M, SOC_ATMEL_SAM0_GCLK0_DIV, 0);
223 }
224 #endif
225 
226 #if !CONFIG_ADC_SAM0 || CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN
227 #define gclk_adc_configure()
228 #else
gclk_adc_configure(void)229 static inline void gclk_adc_configure(void)
230 {
231 	gclk_connect(3, GCLK_GENCTRL_SRC_DFLL48M, SOC_ATMEL_SAM0_GCLK3_DIV, 0);
232 }
233 #endif
234 
235 #if !CONFIG_RTC_ATMEL_SAM0 || CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN
236 #define gclk_rtc_configure()
237 #else
gclk_rtc_configure(void)238 static inline void gclk_rtc_configure(void)
239 {
240 	gclk_connect(4, GCLK_GENCTRL_SRC_XOSC32K, CONFIG_SOC_ATMEL_SAMD_XOSC32K_PRESCALER, 0);
241 }
242 #endif
243 
244 #if !CONFIG_WDT_SAM0
245 #define gclk_wdt_configure()
246 #else
gclk_wdt_configure(void)247 static inline void gclk_wdt_configure(void)
248 {
249 	gclk_connect(2, GCLK_GENCTRL_SRC_OSCULP32K, 4, GCLK_GENCTRL_DIVSEL);
250 }
251 #endif
252 
253 #if CONFIG_SOC_ATMEL_SAMD_OSC8M || CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN
254 #define osc8m_disable()
255 #else
osc8m_disable(void)256 static inline void osc8m_disable(void)
257 {
258 	SYSCTRL->OSC8M.bit.ENABLE = 0;
259 }
260 #endif
261 
soc_reset_hook(void)262 void soc_reset_hook(void)
263 {
264 	osc8m_init();
265 	osc32k_init();
266 	xosc_init();
267 	xosc32k_init();
268 	dfll48m_init();
269 	flash_waitstates_init();
270 	gclk_main_configure();
271 	gclk_adc_configure();
272 	gclk_rtc_configure();
273 	gclk_wdt_configure();
274 	osc8m_disable();
275 }
276 
277 /* clang-format on */
278