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