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