1 /*
2  * Copyright (c) 2019 ML!PA Consulting GmbH
3  * Copyright (c) 2023-2025 Gerson Fernando Budke <nandojve@gmail.com>
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /**
9  * @file
10  * @brief Atmel SAMD MCU series initialization code
11  */
12 
13 /* The CPU clock will be configured to the DT requested value,
14  * and run via DFLL48M.
15  *
16  * Reference -> GCLK Gen 1 -> DFLL48M -> GCLK Gen 0 -> GCLK_MAIN
17  *
18  * GCLK Gen 0 -> GCLK_MAIN @ DPLL0
19  * GCLK Gen 1 -> DFLL48M @ 32768 Hz
20  * GCLK Gen 2 -> USB @ DFLL48M
21  * GCLK Gen 3 -> ADC @ reserved
22  * GCLK Gen 4 -> RTC @ xtal 32768 Hz
23  */
24 
25 #include <zephyr/device.h>
26 #include <zephyr/init.h>
27 #include <zephyr/kernel.h>
28 #include <soc.h>
29 
30 /* clang-format off */
31 
32 #define SAM0_DFLL_FREQ_HZ		(48000000U)
33 #define SAM0_DPLL_FREQ_MIN_HZ		(96000000U)
34 #define SAM0_DPLL_FREQ_MAX_HZ		(200000000U)
35 #define SAM0_XOSC32K_STARTUP_TIME	CONFIG_SOC_ATMEL_SAMD5X_XOSC32K_STARTUP
36 
37 #if !CONFIG_SOC_ATMEL_SAMD5X_XOSC32K
38 #define xosc32k_init()
39 #else
xosc32k_init(void)40 static inline void xosc32k_init(void)
41 {
42 	OSC32KCTRL->XOSC32K.reg = OSC32KCTRL_XOSC32K_ENABLE
43 #if CONFIG_SOC_ATMEL_SAMD5X_XOSC32K_CRYSTAL
44 				| OSC32KCTRL_XOSC32K_XTALEN
45 #endif
46 #if CONFIG_SOC_ATMEL_SAMD5X_XOSC32K_GAIN_HS
47 				| OSC32KCTRL_XOSC32K_CGM_HS
48 #else
49 				| OSC32KCTRL_XOSC32K_CGM_XT
50 #endif
51 				| OSC32KCTRL_XOSC32K_EN32K
52 				| OSC32KCTRL_XOSC32K_EN1K
53 				| OSC32KCTRL_XOSC32K_RUNSTDBY
54 				| OSC32KCTRL_XOSC32K_STARTUP(SAM0_XOSC32K_STARTUP_TIME);
55 
56 	while (!OSC32KCTRL->STATUS.bit.XOSC32KRDY) {
57 	}
58 }
59 #endif
60 
61 #if CONFIG_SOC_ATMEL_SAMD5X_XOSC32K_AS_MAIN
osc32k_init(void)62 static void osc32k_init(void)
63 {
64 	GCLK->GENCTRL[1].reg = GCLK_GENCTRL_SRC(GCLK_SOURCE_XOSC32K)
65 			     | GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN;
66 
67 }
68 #elif CONFIG_SOC_ATMEL_SAMD5X_OSCULP32K_AS_MAIN
osc32k_init(void)69 static void osc32k_init(void)
70 {
71 	GCLK->GENCTRL[1].reg = GCLK_GENCTRL_SRC(GCLK_SOURCE_OSCULP32K)
72 			     | GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN;
73 }
74 #else
75 #error "No Clock Source selected."
76 #endif
77 
dpll_init(uint8_t n,uint32_t f_cpu)78 static void dpll_init(uint8_t n, uint32_t f_cpu)
79 {
80 	/* We source the DPLL from 32kHz GCLK1 */
81 	const uint32_t LDR = ((f_cpu << 5) / SOC_ATMEL_SAM0_OSC32K_FREQ_HZ);
82 
83 	/* disable the DPLL before changing the configuration */
84 	OSCCTRL->Dpll[n].DPLLCTRLA.bit.ENABLE = 0;
85 	while (OSCCTRL->Dpll[n].DPLLSYNCBUSY.reg) {
86 	}
87 
88 	/* set DPLL clock source to 32kHz GCLK1 */
89 	GCLK->PCHCTRL[OSCCTRL_GCLK_ID_FDPLL0 + n].reg = GCLK_PCHCTRL_GEN(1) | GCLK_PCHCTRL_CHEN;
90 	while (!(GCLK->PCHCTRL[OSCCTRL_GCLK_ID_FDPLL0 + n].reg & GCLK_PCHCTRL_CHEN)) {
91 	}
92 
93 	OSCCTRL->Dpll[n].DPLLRATIO.reg  = OSCCTRL_DPLLRATIO_LDRFRAC(LDR & 0x1F)
94 					| OSCCTRL_DPLLRATIO_LDR((LDR >> 5) - 1);
95 
96 	/* Without LBYPASS, startup takes very long, see errata section 2.13. */
97 	OSCCTRL->Dpll[n].DPLLCTRLB.reg	= OSCCTRL_DPLLCTRLB_REFCLK_GCLK
98 					| OSCCTRL_DPLLCTRLB_WUF
99 					| OSCCTRL_DPLLCTRLB_LBYPASS;
100 
101 	OSCCTRL->Dpll[n].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_ENABLE;
102 
103 	while (OSCCTRL->Dpll[n].DPLLSYNCBUSY.reg) {
104 	}
105 	while (!(OSCCTRL->Dpll[n].DPLLSTATUS.bit.CLKRDY &&
106 		 OSCCTRL->Dpll[n].DPLLSTATUS.bit.LOCK)) {
107 	}
108 
109 }
110 
dfll_init(void)111 static void dfll_init(void)
112 {
113 	uint32_t reg = OSCCTRL_DFLLCTRLB_QLDIS
114 #ifdef OSCCTRL_DFLLCTRLB_WAITLOCK
115 		     | OSCCTRL_DFLLCTRLB_WAITLOCK
116 #endif
117 	;
118 
119 	OSCCTRL->DFLLCTRLB.reg = reg;
120 	OSCCTRL->DFLLCTRLA.reg = OSCCTRL_DFLLCTRLA_ENABLE;
121 
122 	while (!OSCCTRL->STATUS.bit.DFLLRDY) {
123 	}
124 }
125 
gclk_reset(void)126 static void gclk_reset(void)
127 {
128 	GCLK->CTRLA.bit.SWRST = 1;
129 	while (GCLK->SYNCBUSY.bit.SWRST) {
130 	}
131 }
132 
gclk_connect(uint8_t gclk,uint8_t src,uint8_t div)133 static void gclk_connect(uint8_t gclk, uint8_t src, uint8_t div)
134 {
135 	GCLK->GENCTRL[gclk].reg = GCLK_GENCTRL_SRC(src)
136 				| GCLK_GENCTRL_DIV(div)
137 				| GCLK_GENCTRL_GENEN;
138 }
139 
soc_reset_hook(void)140 void soc_reset_hook(void)
141 {
142 	uint8_t dfll_div;
143 
144 	if (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC < SAM0_DFLL_FREQ_HZ) {
145 		dfll_div = 3;
146 	} else if (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC < SAM0_DPLL_FREQ_MIN_HZ) {
147 		dfll_div = 2;
148 	} else {
149 		dfll_div = 1;
150 	}
151 
152 	/*
153 	 * Force Cortex M Cache Controller disabled
154 	 *
155 	 * It is not clear if regular Cortex-M instructions can be used to
156 	 * perform cache maintenance or this is a proprietary cache controller
157 	 * that require special SoC support.
158 	 */
159 	CMCC->CTRL.bit.CEN = 0;
160 
161 	gclk_reset();
162 	xosc32k_init();
163 	osc32k_init();
164 	dfll_init();
165 	dpll_init(0, dfll_div * CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC);
166 
167 	/* use DPLL for main clock */
168 	gclk_connect(0, GCLK_SOURCE_DPLL0, dfll_div);
169 
170 	/* connect GCLK2 to 48 MHz DFLL for USB */
171 	gclk_connect(2, GCLK_SOURCE_DFLL48M, 0);
172 
173 	/* connect GCLK4 to xosc32k for RTC. The output is 1024 Hz*/
174 	gclk_connect(4,
175 #if CONFIG_SOC_ATMEL_SAMD5X_XOSC32K
176 		     GCLK_SOURCE_XOSC32K,
177 #else
178 		     GCLK_SOURCE_OSCULP32K,
179 #endif
180 		     CONFIG_SOC_ATMEL_SAMD5X_OSC32K_PRESCALER);
181 }
182 
183 /* clang-format on */
184