1 /*
2 * Copyright (c) 2019 ML!PA Consulting GmbH
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 SAMD 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
18 #define SAM0_DFLL_FREQ_HZ (48000000U)
19 #define SAM0_DPLL_FREQ_MIN_HZ (96000000U)
20 #define SAM0_DPLL_FREQ_MAX_HZ (200000000U)
21 #define SAM0_XOSC32K_STARTUP_TIME CONFIG_SOC_ATMEL_SAMD5X_XOSC32K_STARTUP
22
23 #if CONFIG_SOC_ATMEL_SAMD5X_XOSC32K_AS_MAIN
osc32k_init(void)24 static void osc32k_init(void)
25 {
26 OSC32KCTRL->XOSC32K.reg = OSC32KCTRL_XOSC32K_ENABLE
27 | OSC32KCTRL_XOSC32K_XTALEN
28 | OSC32KCTRL_XOSC32K_CGM_XT
29 | OSC32KCTRL_XOSC32K_EN32K
30 | OSC32KCTRL_XOSC32K_RUNSTDBY
31 | OSC32KCTRL_XOSC32K_STARTUP(SAM0_XOSC32K_STARTUP_TIME)
32 ;
33
34 while (!OSC32KCTRL->STATUS.bit.XOSC32KRDY) {
35 }
36
37 GCLK->GENCTRL[1].reg = GCLK_GENCTRL_SRC(GCLK_SOURCE_XOSC32K)
38 | GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN;
39
40 }
41 #elif CONFIG_SOC_ATMEL_SAMD5X_OSCULP32K_AS_MAIN
osc32k_init(void)42 static void osc32k_init(void)
43 {
44 GCLK->GENCTRL[1].reg = GCLK_GENCTRL_SRC(GCLK_SOURCE_OSCULP32K)
45 | GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN;
46 }
47 #else
48 #error "No Clock Source selected."
49 #endif
50
dpll_init(uint8_t n,uint32_t f_cpu)51 static void dpll_init(uint8_t n, uint32_t f_cpu)
52 {
53 /* We source the DPLL from 32kHz GCLK1 */
54 const uint32_t LDR = ((f_cpu << 5) / SOC_ATMEL_SAM0_OSC32K_FREQ_HZ);
55
56 /* disable the DPLL before changing the configuration */
57 OSCCTRL->Dpll[n].DPLLCTRLA.bit.ENABLE = 0;
58 while (OSCCTRL->Dpll[n].DPLLSYNCBUSY.reg) {
59 }
60
61 /* set DPLL clock source to 32kHz GCLK1 */
62 GCLK->PCHCTRL[OSCCTRL_GCLK_ID_FDPLL0 + n].reg = GCLK_PCHCTRL_GEN(1) | GCLK_PCHCTRL_CHEN;
63 while (!(GCLK->PCHCTRL[OSCCTRL_GCLK_ID_FDPLL0 + n].reg & GCLK_PCHCTRL_CHEN)) {
64 }
65
66 OSCCTRL->Dpll[n].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDRFRAC(LDR & 0x1F)
67 | OSCCTRL_DPLLRATIO_LDR((LDR >> 5) - 1);
68
69 /* Without LBYPASS, startup takes very long, see errata section 2.13. */
70 OSCCTRL->Dpll[n].DPLLCTRLB.reg = OSCCTRL_DPLLCTRLB_REFCLK_GCLK
71 | OSCCTRL_DPLLCTRLB_WUF
72 | OSCCTRL_DPLLCTRLB_LBYPASS;
73
74 OSCCTRL->Dpll[n].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_ENABLE;
75
76 while (OSCCTRL->Dpll[n].DPLLSYNCBUSY.reg) {
77 }
78 while (!(OSCCTRL->Dpll[n].DPLLSTATUS.bit.CLKRDY &&
79 OSCCTRL->Dpll[n].DPLLSTATUS.bit.LOCK)) {
80 }
81
82 }
83
dfll_init(void)84 static void dfll_init(void)
85 {
86 uint32_t reg = OSCCTRL_DFLLCTRLB_QLDIS
87 #ifdef OSCCTRL_DFLLCTRLB_WAITLOCK
88 | OSCCTRL_DFLLCTRLB_WAITLOCK
89 #endif
90 ;
91
92 OSCCTRL->DFLLCTRLB.reg = reg;
93 OSCCTRL->DFLLCTRLA.reg = OSCCTRL_DFLLCTRLA_ENABLE;
94
95 while (!OSCCTRL->STATUS.bit.DFLLRDY) {
96 }
97 }
98
gclk_reset(void)99 static void gclk_reset(void)
100 {
101 GCLK->CTRLA.bit.SWRST = 1;
102 while (GCLK->SYNCBUSY.bit.SWRST) {
103 }
104 }
105
gclk_connect(uint8_t gclk,uint8_t src,uint8_t div)106 static void gclk_connect(uint8_t gclk, uint8_t src, uint8_t div)
107 {
108 GCLK->GENCTRL[gclk].reg = GCLK_GENCTRL_SRC(src)
109 | GCLK_GENCTRL_DIV(div)
110 | GCLK_GENCTRL_GENEN;
111 }
112
soc_reset_hook(void)113 void soc_reset_hook(void)
114 {
115 uint8_t dfll_div;
116
117 if (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC < SAM0_DFLL_FREQ_HZ) {
118 dfll_div = 3;
119 } else if (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC < SAM0_DPLL_FREQ_MIN_HZ) {
120 dfll_div = 2;
121 } else {
122 dfll_div = 1;
123 }
124
125 /*
126 * Force Cortex M Cache Controller disabled
127 *
128 * It is not clear if regular Cortex-M instructions can be used to
129 * perform cache maintenance or this is a proprietary cache controller
130 * that require special SoC support.
131 */
132 CMCC->CTRL.bit.CEN = 0;
133
134 gclk_reset();
135 osc32k_init();
136 dfll_init();
137 dpll_init(0, dfll_div * CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC);
138
139 /* use DPLL for main clock */
140 gclk_connect(0, GCLK_SOURCE_DPLL0, dfll_div);
141
142 /* connect GCLK2 to 48 MHz DFLL for USB */
143 gclk_connect(2, GCLK_SOURCE_DFLL48M, 0);
144 }
145