1 /*
2  * Copyright (c) 2021 Argentum Systems Ltd.
3  * Copyright (c) 2023 Gerson Fernando Budke <nandojve@gmail.com>
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /**
9  * @file
10  * @brief Atmel SAML MCU series initialization code
11  */
12 
13 #include <zephyr/device.h>
14 #include <zephyr/init.h>
15 #include <zephyr/kernel.h>
16 #include <soc.h>
17 #include <cmsis_core.h>
18 
19 /* the SAML21 currently operates only in Performance Level 2... sleep
20  * and low-power operation are not currently supported by the BSP
21  *
22  * the CPU clock will be configured to 48 MHz, and run via DFLL48M.
23  *
24  *   Reference -> GCLK Gen 1 -> DFLL48M -> GCLK Gen 0 -> GCLK_MAIN
25  *
26  * GCLK Gen 0 -> GCLK_MAIN @ 48 Mhz
27  * GCLK Gen 1 -> DFLL48M (variable)
28  * GCLK Gen 2 -> USB @ 48 MHz
29  * GCLK Gen 3 -> ADC @ 24 MHz (further /2 in the ADC peripheral)
30  */
31 
gclk_reset(void)32 static inline void gclk_reset(void)
33 {
34 	/* by default, OSC16M will be enabled at 4 MHz, and the CPU will
35 	 * run from it. to permit initialization, the CPU is temporarily
36 	 * clocked from OSCULP32K, and OSC16M is disabled
37 	 */
38 	GCLK->GENCTRL[0].bit.SRC = GCLK_GENCTRL_SRC_OSCULP32K_Val;
39 	OSCCTRL->OSC16MCTRL.bit.ENABLE = 0;
40 }
41 
42 #if !CONFIG_SOC_ATMEL_SAML_OSC32K
43 #define osc32k_init()
44 #else
osc32k_init(void)45 static inline void osc32k_init(void)
46 {
47 	uint32_t cal;
48 
49 	/* OSC32KCAL is in NVMCTRL_OTP5[12:6] */
50 	cal = *((uint32_t *)NVMCTRL_OTP5);
51 	cal >>= 6;
52 	cal &= (1 << 7) - 1;
53 
54 	OSC32KCTRL->OSC32K.reg = 0
55 		|  OSC32KCTRL_OSC32K_CALIB(cal)
56 		|  OSC32KCTRL_OSC32K_STARTUP(0x5) /* 34 cycles / ~1.038ms */
57 		| !OSC32KCTRL_OSC32K_ONDEMAND
58 		|  OSC32KCTRL_OSC32K_RUNSTDBY
59 		|  OSC32KCTRL_OSC32K_EN32K
60 		|  OSC32KCTRL_OSC32K_ENABLE;
61 
62 	/* wait for ready */
63 	while (!OSC32KCTRL->STATUS.bit.OSC32KRDY) {
64 	}
65 }
66 #endif
67 
68 #if !CONFIG_SOC_ATMEL_SAML_XOSC32K
69 #define xosc32k_init()
70 #else
xosc32k_init(void)71 static inline void xosc32k_init(void)
72 {
73 	OSC32KCTRL->XOSC32K.reg = 0
74 		|  OSC32KCTRL_XOSC32K_STARTUP(0x1) /* 4096 cycles / ~0.13s */
75 		| !OSC32KCTRL_XOSC32K_ONDEMAND
76 		|  OSC32KCTRL_XOSC32K_RUNSTDBY
77 		|  OSC32KCTRL_XOSC32K_EN32K
78 #if CONFIG_SOC_ATMEL_SAML_XOSC32K_CRYSTAL
79 		|  OSC32KCTRL_XOSC32K_XTALEN
80 #endif
81 		|  OSC32KCTRL_XOSC32K_ENABLE;
82 
83 	/* wait for ready */
84 	while (!OSC32KCTRL->STATUS.bit.XOSC32KRDY) {
85 	}
86 }
87 #endif
88 
89 #if !CONFIG_SOC_ATMEL_SAML_OSC16M
90 #define osc16m_init()
91 #else
osc16m_init(void)92 static inline void osc16m_init(void)
93 {
94 	OSCCTRL->OSC16MCTRL.reg = 0
95 		| !OSCCTRL_OSC16MCTRL_ONDEMAND
96 		|  OSCCTRL_OSC16MCTRL_RUNSTDBY
97 		|  OSCCTRL_OSC16MCTRL_FSEL_16
98 		|  OSCCTRL_OSC16MCTRL_ENABLE;
99 
100 	/* wait for ready */
101 	while (!OSCCTRL->STATUS.bit.OSC16MRDY) {
102 	}
103 }
104 #endif
105 
106 /* TODO: use CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC ?? */
dfll48m_init(void)107 static inline void dfll48m_init(void)
108 {
109 	uint32_t cal;
110 
111 	/* setup the reference clock (if any) */
112 	GCLK->GENCTRL[1].reg = 0
113 #if CONFIG_SOC_ATMEL_SAML_OSC32K_AS_MAIN
114 		|  GCLK_GENCTRL_SRC_OSC32K
115 #elif CONFIG_SOC_ATMEL_SAML_XOSC32K_AS_MAIN
116 		|  GCLK_GENCTRL_SRC_XOSC32K
117 #elif CONFIG_SOC_ATMEL_SAML_OSC16M_AS_MAIN
118 		/* configure Fout = Fin / 2^(DIV+1) = 31.25 kHz
119 		 * Fgclk_dfll48m_ref max is 33 kHz
120 		 */
121 		|  GCLK_GENCTRL_DIV(8)
122 		|  GCLK_GENCTRL_DIVSEL
123 		|  GCLK_GENCTRL_SRC_OSC16M
124 #endif
125 #if !CONFIG_SOC_ATMEL_SAML_OPENLOOP_AS_MAIN
126 		|  GCLK_GENCTRL_RUNSTDBY
127 		|  GCLK_GENCTRL_GENEN
128 #endif
129 		;
130 
131 #if !CONFIG_SOC_ATMEL_SAML_OPENLOOP_AS_MAIN
132 	/* configure and enable the generator & peripheral channel */
133 	GCLK->PCHCTRL[0].reg = 0
134 		|  GCLK_PCHCTRL_CHEN
135 		|  GCLK_PCHCTRL_GEN_GCLK1;
136 #endif
137 
138 	/* --- */
139 
140 	/* if the target frequency is 48 MHz, then the calibration value can be used to
141 	 * decrease the time until the coarse lock is acquired. this is loaded from
142 	 * NVMCTRL_OTP5[31:26]
143 	 */
144 	cal = *((uint32_t *)NVMCTRL_OTP5);
145 	cal >>= 26;
146 	cal &= (1 << 6) - 1;
147 
148 	OSCCTRL->DFLLCTRL.reg = 0
149 		|  OSCCTRL_DFLLCTRL_QLDIS
150 		| !OSCCTRL_DFLLCTRL_ONDEMAND
151 		|  OSCCTRL_DFLLCTRL_RUNSTDBY
152 #if !CONFIG_SOC_ATMEL_SAML_OPENLOOP_AS_MAIN
153 		|  OSCCTRL_DFLLCTRL_MODE
154 #endif
155 		;
156 
157 	OSCCTRL->DFLLVAL.reg = 0
158 		|  OSCCTRL_DFLLVAL_COARSE(cal)
159 		|  OSCCTRL_DFLLVAL_FINE(512) /* use 50% */
160 		;
161 
162 	OSCCTRL->DFLLMUL.reg = 0
163 		/* use 25% of maximum value for the coarse and fine step
164 		 * ... I couldn't find details on the inner workings of the DFLL, or any
165 		 * example values for these - I have seen others using ~50%. hopefully these
166 		 * values will provide a good balance between startup time and overshoot
167 		 */
168 		|  OSCCTRL_DFLLMUL_CSTEP(16)
169 		|  OSCCTRL_DFLLMUL_FSTEP(256)
170 #if CONFIG_SOC_ATMEL_SAML_OSC32K_AS_MAIN || CONFIG_SOC_ATMEL_SAML_XOSC32K_AS_MAIN
171 		/* use a 32.768 kHz reference ... 48e6 / 32,768 = 1,464.843... */
172 		|  OSCCTRL_DFLLMUL_MUL(1465)
173 #elif CONFIG_SOC_ATMEL_SAML_OSC16M_AS_MAIN
174 		/* use a 16 MHz -> 31.25 kHz reference... 48e6 / 31,250 = 1,536
175 		 * a small value can make the DFLL unstable, hence not using the
176 		 * 16 MHz source directly
177 		 */
178 		|  OSCCTRL_DFLLMUL_MUL(1536)
179 #endif
180 		;
181 
182 	/* --- */
183 
184 	/* enable */
185 	while (!OSCCTRL->STATUS.bit.DFLLRDY) {
186 	}
187 	OSCCTRL->DFLLCTRL.bit.ENABLE = 1;
188 
189 #if !CONFIG_SOC_ATMEL_SAML_OPENLOOP_AS_MAIN
190 	/* wait for ready... note in open loop mode, we won't get a lock */
191 	while (!OSCCTRL->STATUS.bit.DFLLLCKC || !OSCCTRL->STATUS.bit.DFLLLCKF) {
192 	}
193 #endif
194 }
195 
flash_waitstates_init(void)196 static inline void flash_waitstates_init(void)
197 {
198 	/* PL2, >= 2.7v, 48MHz = 2 wait states */
199 	NVMCTRL->CTRLB.bit.RWS = 2;
200 }
201 
pm_init(void)202 static inline void pm_init(void)
203 {
204 	PM->PLCFG.bit.PLDIS = 0;
205 	PM->PLCFG.bit.PLSEL = 2;
206 }
207 
gclk_main_configure(void)208 static inline void gclk_main_configure(void)
209 {
210 	/* finally, switch the CPU over to run from DFLL48M */
211 	GCLK->GENCTRL[0].bit.SRC = GCLK_GENCTRL_SRC_DFLL48M_Val;
212 }
213 
214 #if !CONFIG_USB_DC_SAM0
215 #define gclk_usb_configure()
216 #else
gclk_usb_configure(void)217 static inline void gclk_usb_configure(void)
218 {
219 	GCLK->GENCTRL[2].reg = 0
220 		| GCLK_GENCTRL_SRC_DFLL48M
221 		| GCLK_GENCTRL_DIV(1)
222 		| GCLK_GENCTRL_GENEN;
223 }
224 #endif
225 
226 #if !CONFIG_ADC_SAM0
227 #define gclk_adc_configure()
228 #else
gclk_adc_configure(void)229 static inline void gclk_adc_configure(void)
230 {
231 	GCLK->GENCTRL[3].reg = 0
232 		| GCLK_GENCTRL_SRC_DFLL48M
233 		| GCLK_GENCTRL_DIV(2)
234 		| GCLK_GENCTRL_GENEN;
235 }
236 #endif
237 
238 #if CONFIG_SOC_ATMEL_SAML_DEBUG_PAUSE
pause_for_debug(void)239 static inline void pause_for_debug(void)
240 {
241 	/* for some reason, when attempting to flash / debug the target, the operations
242 	 * will time out... I suspect this is due to clock configuration, so instead of
243 	 * doing this immediately, we defer startup for a while to permit the debugger
244 	 * to jump in and interrupt us. ick
245 	 */
246 	for (uint32_t i = 0; i < 10000; i += 1) {
247 		__asm__ volatile ("nop\n");
248 	}
249 }
250 #else
pause_for_debug(void)251 static inline void pause_for_debug(void) {}
252 #endif
253 
soc_reset_hook(void)254 void soc_reset_hook(void)
255 {
256 	pause_for_debug();
257 
258 	gclk_reset();
259 	osc32k_init();
260 	xosc32k_init();
261 	osc16m_init();
262 	dfll48m_init();
263 	flash_waitstates_init();
264 	pm_init();
265 	gclk_main_configure();
266 	gclk_usb_configure();
267 	gclk_adc_configure();
268 }
269