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