/* * Copyright (c) 2017 Google LLC. * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * @brief Atmel SAMD MCU series initialization code */ #include #include #include #include #include #include static void flash_waitstates_init(void) { /* One wait state at 48 MHz. */ NVMCTRL->CTRLB.bit.RWS = NVMCTRL_CTRLB_RWS_HALF_Val; } /* Follows the same structure as the Arduino Zero, namely: * XOSC32K -> GCLK1 -> DFLL48M -> GCLK0 * OSC8M -> 8 MHz -> GCLK3 */ static void xosc_init(void) { #ifdef CONFIG_SOC_ATMEL_SAMD_XOSC #error External oscillator support is not implemented. #endif } static void wait_gclk_synchronization(void) { while (GCLK->STATUS.bit.SYNCBUSY) { } } static void xosc32k_init(void) { #ifdef CONFIG_SOC_ATMEL_SAMD_XOSC32K SYSCTRL->XOSC32K.reg = SYSCTRL_XOSC32K_STARTUP(6) | SYSCTRL_XOSC32K_XTALEN | SYSCTRL_XOSC32K_EN32K; SYSCTRL->XOSC32K.bit.ENABLE = 1; /* Wait for the crystal to stabalise. */ while (!SYSCTRL->PCLKSR.bit.XOSC32KRDY) { } #endif } static void osc32k_init(void) { #ifdef FUSES_OSC32K_CAL_ADDR uint32_t fuse = *(uint32_t *)FUSES_OSC32K_CAL_ADDR; uint32_t calib = (fuse & FUSES_OSC32K_CAL_Msk) >> FUSES_OSC32K_CAL_Pos; #else uint32_t fuse = *(uint32_t *)FUSES_OSC32KCAL_ADDR; uint32_t calib = (fuse & FUSES_OSC32KCAL_Msk) >> FUSES_OSC32KCAL_Pos; #endif SYSCTRL->OSC32K.reg = SYSCTRL_OSC32K_CALIB(calib) | SYSCTRL_OSC32K_STARTUP(0x6u) | SYSCTRL_OSC32K_EN32K | SYSCTRL_OSC32K_ENABLE; /* Wait for the oscillator to stabalise. */ while (!SYSCTRL->PCLKSR.bit.OSC32KRDY) { } } static void dfll_init(void) { /* No prescaler */ GCLK->GENDIV.reg = GCLK_GENDIV_ID(1) | GCLK_GENDIV_DIV(0); wait_gclk_synchronization(); #if defined(CONFIG_SOC_ATMEL_SAMD_XOSC32K_AS_MAIN) /* Route XOSC32K to GCLK1 */ GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(1) | GCLK_GENCTRL_SRC_XOSC32K | GCLK_GENCTRL_GENEN; #elif defined(CONFIG_SOC_ATMEL_SAMD_OSC8M_AS_MAIN) /* Route OSC8M to GCLK1 */ GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(1) | GCLK_GENCTRL_SRC_OSC8M | GCLK_GENCTRL_GENEN; #else #error Unsupported main clock source. #endif wait_gclk_synchronization(); /* Route GCLK1 to multiplexer 1 */ GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(0) | GCLK_CLKCTRL_GEN_GCLK1 | GCLK_CLKCTRL_CLKEN; wait_gclk_synchronization(); SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_ENABLE; while (!SYSCTRL->PCLKSR.bit.DFLLRDY) { } uint32_t mul = (SOC_ATMEL_SAM0_MCK_FREQ_HZ + SOC_ATMEL_SAM0_GCLK1_FREQ_HZ / 2) / SOC_ATMEL_SAM0_GCLK1_FREQ_HZ; SYSCTRL->DFLLMUL.reg = SYSCTRL_DFLLMUL_CSTEP(31) | SYSCTRL_DFLLMUL_FSTEP(511) | SYSCTRL_DFLLMUL_MUL(mul); while (!SYSCTRL->PCLKSR.bit.DFLLRDY) { } SYSCTRL->DFLLCTRL.reg |= SYSCTRL_DFLLCTRL_MODE | #ifdef SYSCTRL_DFLLCTRL_WAITLOCK SYSCTRL_DFLLCTRL_WAITLOCK | #endif SYSCTRL_DFLLCTRL_QLDIS; while (!SYSCTRL->PCLKSR.bit.DFLLRDY) { } /* Enable the DFLL */ SYSCTRL->DFLLCTRL.bit.ENABLE = 1; while (!SYSCTRL->PCLKSR.bit.DFLLLCKC || !SYSCTRL->PCLKSR.bit.DFLLLCKF) { } while (!SYSCTRL->PCLKSR.bit.DFLLRDY) { } } static void osc8m_init(void) { /* Turn off the prescaler */ SYSCTRL->OSC8M.bit.PRESC = SYSCTRL_OSC8M_PRESC(0); SYSCTRL->OSC8M.bit.ONDEMAND = 0; } static void gclks_init(void) { /* DFLL/1 -> GCLK0 */ GCLK->GENDIV.reg = GCLK_GENDIV_ID(0) | GCLK_GENDIV_DIV(0); wait_gclk_synchronization(); GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(0) | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_IDC | GCLK_GENCTRL_GENEN; wait_gclk_synchronization(); /* OSC8M/1 -> GCLK3 */ GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(3) | GCLK_GENCTRL_SRC_OSC8M | GCLK_GENCTRL_GENEN; wait_gclk_synchronization(); /* OSCULP32K/32 -> GCLK2 */ GCLK->GENDIV.reg = GCLK_GENDIV_ID(2) | GCLK_GENDIV_DIV(32 - 1); wait_gclk_synchronization(); GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(2) | GCLK_GENCTRL_SRC_OSC32K | GCLK_GENCTRL_GENEN; wait_gclk_synchronization(); } static void dividers_init(void) { /* Set the CPU, APBA, B, and C dividers */ PM->CPUSEL.reg = PM_CPUSEL_CPUDIV_DIV1; PM->APBASEL.reg = PM_APBASEL_APBADIV_DIV1_Val; PM->APBBSEL.reg = PM_APBBSEL_APBBDIV_DIV1_Val; PM->APBCSEL.reg = PM_APBCSEL_APBCDIV_DIV1_Val; /* TODO(mlhx): enable clock failure detection? */ } static int atmel_samd_init(const struct device *arg) { uint32_t key; ARG_UNUSED(arg); key = irq_lock(); flash_waitstates_init(); osc8m_init(); osc32k_init(); xosc_init(); xosc32k_init(); dfll_init(); gclks_init(); dividers_init(); /* Install default handler that simply resets the CPU * if configured in the kernel, NOP otherwise */ NMI_INIT(); irq_unlock(key); return 0; } SYS_INIT(atmel_samd_init, PRE_KERNEL_1, 0);