1 /* 2 * Copyright (c) 2017 Google LLC. 3 * Copyright (c) 2023 Ionut Catalin Pavel <iocapa@iocapa.com> 4 * Copyright (c) 2023 Gerson Fernando Budke <nandojve@gmail.com> 5 * 6 * SPDX-License-Identifier: Apache-2.0 7 */ 8 9 /** 10 * @file 11 * @brief Atmel SAMD MCU series initialization code 12 */ 13 14 /* The CPU clock will be configured to the DT requested value, 15 * and run via DFLL48M. 16 * 17 * Reference -> GCLK Gen 1 -> DFLL48M -> GCLK Gen 0 -> GCLK_MAIN 18 * 19 * GCLK Gen 0 -> GCLK_MAIN 20 * GCLK Gen 1 -> DFLL48M (variable) 21 * GCLK Gen 2 -> WDT @ 32768 Hz 22 * GCLK Gen 3 -> ADC @ 8 MHz 23 */ 24 25 #include <zephyr/device.h> 26 #include <zephyr/init.h> 27 #include <zephyr/kernel.h> 28 29 #include <soc.h> 30 #include <cmsis_core.h> 31 32 /** 33 * Fix different naming conventions for SAMD20 34 */ 35 #ifdef FUSES_OSC32KCAL_ADDR 36 #define FUSES_OSC32K_CAL_ADDR FUSES_OSC32KCAL_ADDR 37 #define FUSES_OSC32K_CAL_Pos FUSES_OSC32KCAL_Pos 38 #define FUSES_OSC32K_CAL_Msk FUSES_OSC32KCAL_Msk 39 #endif 40 osc8m_init(void)41static inline void osc8m_init(void) 42 { 43 uint32_t reg; 44 45 /* Save calibration */ 46 reg = SYSCTRL->OSC8M.reg 47 & (SYSCTRL_OSC8M_FRANGE_Msk | SYSCTRL_OSC8M_CALIB_Msk); 48 49 SYSCTRL->OSC8M.reg = reg 50 | SYSCTRL_OSC8M_RUNSTDBY 51 | SYSCTRL_OSC8M_PRESC(0) /* 8MHz (/1) */ 52 | SYSCTRL_OSC8M_ENABLE; 53 54 while (!SYSCTRL->PCLKSR.bit.OSC8MRDY) { 55 } 56 57 /* Use 8Mhz clock as gclk_main to allow switching between clocks 58 * when using bootloaders 59 */ 60 GCLK->GENDIV.reg = GCLK_GENDIV_ID(0) 61 | GCLK_GENDIV_DIV(0); 62 63 while (GCLK->STATUS.bit.SYNCBUSY) { 64 } 65 66 GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(0) 67 | GCLK_GENCTRL_SRC_OSC8M 68 | GCLK_GENCTRL_IDC 69 | GCLK_GENCTRL_GENEN; 70 71 while (GCLK->STATUS.bit.SYNCBUSY) { 72 } 73 } 74 75 #if !CONFIG_SOC_ATMEL_SAMD_OSC32K || CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN 76 #define osc32k_init() 77 #else osc32k_init(void)78static inline void osc32k_init(void) 79 { 80 uint32_t cal; 81 82 /* Get calibration value */ 83 cal = (*((uint32_t *)FUSES_OSC32K_CAL_ADDR) 84 & FUSES_OSC32K_CAL_Msk) >> FUSES_OSC32K_CAL_Pos; 85 86 SYSCTRL->OSC32K.reg = SYSCTRL_OSC32K_CALIB(cal) 87 | SYSCTRL_OSC32K_STARTUP(0x5) /* 34 cycles / ~1ms */ 88 | SYSCTRL_OSC32K_RUNSTDBY 89 | SYSCTRL_OSC32K_EN32K 90 | SYSCTRL_OSC32K_ENABLE; 91 92 while (!SYSCTRL->PCLKSR.bit.OSC32KRDY) { 93 } 94 } 95 #endif 96 97 #if !CONFIG_SOC_ATMEL_SAMD_XOSC || CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN 98 #define xosc_init() 99 #else xosc_init(void)100static inline void xosc_init(void) 101 { 102 SYSCTRL->XOSC.reg = SYSCTRL_XOSC_STARTUP(0x5) /* 32 cycles / ~1ms */ 103 | SYSCTRL_XOSC_RUNSTDBY 104 | SYSCTRL_XOSC_AMPGC 105 #if CONFIG_SOC_ATMEL_SAMD_XOSC_FREQ_HZ <= 2000000 106 | SYSCTRL_XOSC_GAIN(0x0) 107 #elif CONFIG_SOC_ATMEL_SAMD_XOSC_FREQ_HZ <= 4000000 108 | SYSCTRL_XOSC_GAIN(0x1) 109 #elif CONFIG_SOC_ATMEL_SAMD_XOSC_FREQ_HZ <= 8000000 110 | SYSCTRL_XOSC_GAIN(0x2) 111 #elif CONFIG_SOC_ATMEL_SAMD_XOSC_FREQ_HZ <= 16000000 112 | SYSCTRL_XOSC_GAIN(0x3) 113 #elif CONFIG_SOC_ATMEL_SAMD_XOSC_FREQ_HZ <= 32000000 114 | SYSCTRL_XOSC_GAIN(0x4) 115 #endif 116 #if CONFIG_SOC_ATMEL_SAMD_XOSC_CRYSTAL 117 | SYSCTRL_XOSC_XTALEN 118 #endif 119 | SYSCTRL_XOSC_ENABLE; 120 121 while (!SYSCTRL->PCLKSR.bit.XOSCRDY) { 122 } 123 } 124 #endif 125 126 #if !CONFIG_SOC_ATMEL_SAMD_XOSC32K || CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN 127 #define xosc32k_init() 128 #else xosc32k_init(void)129static inline void xosc32k_init(void) 130 { 131 SYSCTRL->XOSC32K.reg = SYSCTRL_XOSC32K_STARTUP(0x1) /* 4096 cycles / ~0.13s */ 132 | SYSCTRL_XOSC32K_RUNSTDBY 133 | SYSCTRL_XOSC32K_EN32K 134 | SYSCTRL_XOSC32K_AAMPEN 135 #if CONFIG_SOC_ATMEL_SAMD_XOSC32K_CRYSTAL 136 | SYSCTRL_XOSC32K_XTALEN 137 #endif 138 | SYSCTRL_XOSC32K_ENABLE; 139 140 while (!SYSCTRL->PCLKSR.bit.XOSC32KRDY) { 141 } 142 } 143 #endif 144 145 #if CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN 146 #define dfll48m_init() 147 #else dfll48m_init(void)148static inline void dfll48m_init(void) 149 { 150 uint32_t fcal, ccal; 151 152 GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(1) 153 #if CONFIG_SOC_ATMEL_SAMD_OSC32K_AS_MAIN 154 | GCLK_GENCTRL_SRC_OSC32K 155 #elif CONFIG_SOC_ATMEL_SAMD_XOSC32K_AS_MAIN 156 | GCLK_GENCTRL_SRC_XOSC32K 157 #elif CONFIG_SOC_ATMEL_SAMD_OSC8M_AS_MAIN 158 | GCLK_GENCTRL_SRC_OSC8M 159 #elif CONFIG_SOC_ATMEL_SAMD_XOSC_AS_MAIN 160 | GCLK_GENCTRL_SRC_XOSC 161 #endif 162 | GCLK_GENCTRL_IDC 163 | GCLK_GENCTRL_RUNSTDBY 164 | GCLK_GENCTRL_GENEN; 165 166 while (GCLK->STATUS.bit.SYNCBUSY) { 167 } 168 169 GCLK->GENDIV.reg = GCLK_GENDIV_ID(1) 170 | GCLK_GENDIV_DIV(SOC_ATMEL_SAM0_GCLK1_DIV); 171 172 while (GCLK->STATUS.bit.SYNCBUSY) { 173 } 174 175 /* Route multiplexer 0 to DFLL48M */ 176 GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(0) 177 | GCLK_CLKCTRL_GEN_GCLK1 178 | GCLK_CLKCTRL_CLKEN; 179 180 181 SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_MODE 182 | SYSCTRL_DFLLCTRL_QLDIS 183 | SYSCTRL_DFLLCTRL_RUNSTDBY; 184 185 /* Get calibration values */ 186 ccal = (*((uint32_t *)FUSES_DFLL48M_COARSE_CAL_ADDR) 187 & FUSES_DFLL48M_COARSE_CAL_Msk) >> FUSES_DFLL48M_COARSE_CAL_Pos; 188 189 fcal = (*((uint32_t *)FUSES_DFLL48M_FINE_CAL_ADDR) 190 & FUSES_DFLL48M_FINE_CAL_Msk) >> FUSES_DFLL48M_FINE_CAL_Pos; 191 192 SYSCTRL->DFLLVAL.reg = SYSCTRL_DFLLVAL_COARSE(ccal) 193 | SYSCTRL_DFLLVAL_FINE(fcal); 194 195 /* Use half of maximum for both */ 196 SYSCTRL->DFLLMUL.reg = SYSCTRL_DFLLMUL_CSTEP(31) 197 | SYSCTRL_DFLLMUL_FSTEP(511) 198 | SYSCTRL_DFLLMUL_MUL(SOC_ATMEL_SAM0_DFLL48M_MUL); 199 200 /* Enable */ 201 while (!SYSCTRL->PCLKSR.bit.DFLLRDY) { 202 } 203 SYSCTRL->DFLLCTRL.bit.ENABLE = 1; 204 205 /* Wait for synchronization. */ 206 while (!SYSCTRL->PCLKSR.bit.DFLLLCKC || !SYSCTRL->PCLKSR.bit.DFLLLCKF) { 207 } 208 } 209 #endif 210 211 #if CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN 212 #define flash_waitstates_init() 213 #else flash_waitstates_init(void)214static inline void flash_waitstates_init(void) 215 { 216 NVMCTRL->CTRLB.bit.RWS = NVMCTRL_CTRLB_RWS(CONFIG_SOC_ATMEL_SAMD_NVM_WAIT_STATES); 217 } 218 #endif 219 220 #if CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN 221 #define gclk_main_configure() 222 #else gclk_main_configure(void)223static inline void gclk_main_configure(void) 224 { 225 GCLK->GENDIV.reg = GCLK_GENDIV_ID(0) 226 | GCLK_GENDIV_DIV(SOC_ATMEL_SAM0_GCLK0_DIV); 227 228 while (GCLK->STATUS.bit.SYNCBUSY) { 229 } 230 231 GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(0) 232 | GCLK_GENCTRL_SRC_DFLL48M 233 | GCLK_GENCTRL_IDC 234 | GCLK_GENCTRL_GENEN; 235 236 while (GCLK->STATUS.bit.SYNCBUSY) { 237 } 238 } 239 #endif 240 241 #if !CONFIG_ADC_SAM0 || CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN 242 #define gclk_adc_configure() 243 #else gclk_adc_configure(void)244static inline void gclk_adc_configure(void) 245 { 246 GCLK->GENDIV.reg = GCLK_GENDIV_ID(3) 247 | GCLK_GENDIV_DIV(SOC_ATMEL_SAM0_GCLK3_DIV); 248 249 while (GCLK->STATUS.bit.SYNCBUSY) { 250 } 251 252 GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(3) 253 | GCLK_GENCTRL_SRC_DFLL48M 254 | GCLK_GENCTRL_IDC 255 | GCLK_GENCTRL_GENEN; 256 257 while (GCLK->STATUS.bit.SYNCBUSY) { 258 } 259 } 260 #endif 261 262 #if !CONFIG_WDT_SAM0 263 #define gclk_wdt_configure() 264 #else gclk_wdt_configure(void)265static inline void gclk_wdt_configure(void) 266 { 267 GCLK->GENDIV.reg = GCLK_GENDIV_ID(2) 268 | GCLK_GENDIV_DIV(4); 269 270 GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(2) 271 | GCLK_GENCTRL_GENEN 272 | GCLK_GENCTRL_SRC_OSCULP32K 273 | GCLK_GENCTRL_DIVSEL; 274 275 while (GCLK->STATUS.bit.SYNCBUSY) { 276 } 277 } 278 #endif 279 280 #if CONFIG_SOC_ATMEL_SAMD_OSC8M || CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN 281 #define osc8m_disable() 282 #else osc8m_disable(void)283static inline void osc8m_disable(void) 284 { 285 SYSCTRL->OSC8M.bit.ENABLE = 0; 286 } 287 #endif 288 soc_reset_hook(void)289void soc_reset_hook(void) 290 { 291 osc8m_init(); 292 osc32k_init(); 293 xosc_init(); 294 xosc32k_init(); 295 dfll48m_init(); 296 flash_waitstates_init(); 297 gclk_main_configure(); 298 gclk_adc_configure(); 299 gclk_wdt_configure(); 300 osc8m_disable(); 301 } 302