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