| /* |
| * Copyright (c) 2021 Argentum Systems Ltd. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** |
| * @file |
| * @brief Atmel SAML MCU series initialization code |
| */ |
| |
| #include <zephyr/arch/cpu.h> |
| #include <zephyr/arch/arm/aarch32/cortex_m/cmsis.h> |
| #include <zephyr/device.h> |
| #include <zephyr/init.h> |
| #include <zephyr/kernel.h> |
| #include <soc.h> |
| |
| /* the SAML21 currently operates only in Performance Level 2... sleep |
| * and low-power operation are not currently supported by the BSP |
| * |
| * the CPU clock will be configured to 48 MHz, and run via DFLL48M. |
| * |
| * Reference -> GCLK Gen 1 -> DFLL48M -> GCLK Gen 0 -> GCLK_MAIN |
| * |
| * GCLK Gen 0 -> GCLK_MAIN @ 48 Mhz |
| * GCLK Gen 1 -> DFLL48M (variable) |
| * GCLK Gen 2 -> USB @ 48 MHz |
| * GCLK Gen 3 -> ADC @ 24 MHz (further /2 in the ADC peripheral) |
| */ |
| |
| static inline void gclk_reset(void) |
| { |
| GCLK->CTRLA.bit.SWRST = 1; |
| while (GCLK->SYNCBUSY.bit.SWRST) { |
| } |
| |
| /* by default, OSC16M will be enabled at 4 MHz, and the CPU will |
| * run from it. to permit initialization, the CPU is temporarily |
| * clocked from OSCULP32K, and OSC16M is disabled |
| */ |
| GCLK->GENCTRL[0].bit.SRC = GCLK_GENCTRL_SRC_OSCULP32K_Val; |
| OSCCTRL->OSC16MCTRL.bit.ENABLE = 0; |
| } |
| |
| #if !CONFIG_SOC_ATMEL_SAML_OSC32K |
| #define osc32k_init() |
| #else |
| static inline void osc32k_init(void) |
| { |
| uint32_t cal; |
| |
| /* OSC32KCAL is in NVMCTRL_OTP5[12:6] */ |
| cal = *((uint32_t *)NVMCTRL_OTP5); |
| cal >>= 6; |
| cal &= (1 << 7) - 1; |
| |
| OSC32KCTRL->OSC32K.reg = 0 |
| | OSC32KCTRL_OSC32K_CALIB(cal) |
| | OSC32KCTRL_OSC32K_STARTUP(0x5) /* 34 cycles / ~1.038ms */ |
| | !OSC32KCTRL_OSC32K_ONDEMAND |
| | OSC32KCTRL_OSC32K_RUNSTDBY |
| | OSC32KCTRL_OSC32K_EN32K |
| | OSC32KCTRL_OSC32K_ENABLE; |
| |
| /* wait for ready */ |
| while (!OSC32KCTRL->STATUS.bit.OSC32KRDY) { |
| } |
| } |
| #endif |
| |
| #if !CONFIG_SOC_ATMEL_SAML_XOSC32K |
| #define xosc32k_init() |
| #else |
| static inline void xosc32k_init(void) |
| { |
| OSC32KCTRL->XOSC32K.reg = 0 |
| | OSC32KCTRL_XOSC32K_STARTUP(0x1) /* 4096 cycles / ~0.13s */ |
| | !OSC32KCTRL_XOSC32K_ONDEMAND |
| | OSC32KCTRL_XOSC32K_RUNSTDBY |
| | OSC32KCTRL_XOSC32K_EN32K |
| #if CONFIG_SOC_ATMEL_SAML_XOSC32K_CRYSTAL |
| | OSC32KCTRL_XOSC32K_XTALEN |
| #endif |
| | OSC32KCTRL_XOSC32K_ENABLE; |
| |
| /* wait for ready */ |
| while (!OSC32KCTRL->STATUS.bit.XOSC32KRDY) { |
| } |
| } |
| #endif |
| |
| #if !CONFIG_SOC_ATMEL_SAML_OSC16M |
| #define osc16m_init() |
| #else |
| static inline void osc16m_init(void) |
| { |
| OSCCTRL->OSC16MCTRL.reg = 0 |
| | !OSCCTRL_OSC16MCTRL_ONDEMAND |
| | OSCCTRL_OSC16MCTRL_RUNSTDBY |
| | OSCCTRL_OSC16MCTRL_FSEL_16 |
| | OSCCTRL_OSC16MCTRL_ENABLE; |
| |
| /* wait for ready */ |
| while (!OSCCTRL->STATUS.bit.OSC16MRDY) { |
| } |
| } |
| #endif |
| |
| /* TODO: use CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC ?? */ |
| static inline void dfll48m_init(void) |
| { |
| uint32_t cal; |
| |
| /* setup the reference clock (if any) */ |
| GCLK->GENCTRL[1].reg = 0 |
| #if CONFIG_SOC_ATMEL_SAML_OSC32K_AS_MAIN |
| | GCLK_GENCTRL_SRC_OSC32K |
| #elif CONFIG_SOC_ATMEL_SAML_XOSC32K_AS_MAIN |
| | GCLK_GENCTRL_SRC_XOSC32K |
| #elif CONFIG_SOC_ATMEL_SAML_OSC16M_AS_MAIN |
| /* configure Fout = Fin / 2^(DIV+1) = 31.25 kHz |
| * Fgclk_dfll48m_ref max is 33 kHz |
| */ |
| | GCLK_GENCTRL_DIV(8) |
| | GCLK_GENCTRL_DIVSEL |
| | GCLK_GENCTRL_SRC_OSC16M |
| #endif |
| #if !CONFIG_SOC_ATMEL_SAML_OPENLOOP_AS_MAIN |
| | GCLK_GENCTRL_RUNSTDBY |
| | GCLK_GENCTRL_GENEN |
| #endif |
| ; |
| |
| #if !CONFIG_SOC_ATMEL_SAML_OPENLOOP_AS_MAIN |
| /* configure and enable the generator & peripheral channel */ |
| GCLK->PCHCTRL[0].reg = 0 |
| | GCLK_PCHCTRL_CHEN |
| | GCLK_PCHCTRL_GEN_GCLK1; |
| #endif |
| |
| /* --- */ |
| |
| /* if the target frequency is 48 MHz, then the calibration value can be used to |
| * decrease the time until the coarse lock is acquired. this is loaded from |
| * NVMCTRL_OTP5[31:26] |
| */ |
| cal = *((uint32_t *)NVMCTRL_OTP5); |
| cal >>= 26; |
| cal &= (1 << 6) - 1; |
| |
| OSCCTRL->DFLLCTRL.reg = 0 |
| | OSCCTRL_DFLLCTRL_QLDIS |
| | !OSCCTRL_DFLLCTRL_ONDEMAND |
| | OSCCTRL_DFLLCTRL_RUNSTDBY |
| #if !CONFIG_SOC_ATMEL_SAML_OPENLOOP_AS_MAIN |
| | OSCCTRL_DFLLCTRL_MODE |
| #endif |
| ; |
| |
| OSCCTRL->DFLLVAL.reg = 0 |
| | OSCCTRL_DFLLVAL_COARSE(cal) |
| | OSCCTRL_DFLLVAL_FINE(512) /* use 50% */ |
| ; |
| |
| OSCCTRL->DFLLMUL.reg = 0 |
| /* use 25% of maximum value for the coarse and fine step |
| * ... I couldn't find details on the inner workings of the DFLL, or any |
| * example values for these - I have seen others using ~50%. hopefully these |
| * values will provide a good balance between startup time and overshoot |
| */ |
| | OSCCTRL_DFLLMUL_CSTEP(16) |
| | OSCCTRL_DFLLMUL_FSTEP(256) |
| #if CONFIG_SOC_ATMEL_SAML_OSC32K_AS_MAIN || CONFIG_SOC_ATMEL_SAML_XOSC32K_AS_MAIN |
| /* use a 32.768 kHz reference ... 48e6 / 32,768 = 1,464.843... */ |
| | OSCCTRL_DFLLMUL_MUL(1465) |
| #elif CONFIG_SOC_ATMEL_SAML_OSC16M_AS_MAIN |
| /* use a 16 MHz -> 31.25 kHz reference... 48e6 / 31,250 = 1,536 |
| * a small value can make the DFLL unstable, hence not using the |
| * 16 MHz source directly |
| */ |
| | OSCCTRL_DFLLMUL_MUL(1536) |
| #endif |
| ; |
| |
| /* --- */ |
| |
| /* enable */ |
| while (!OSCCTRL->STATUS.bit.DFLLRDY) { |
| } |
| OSCCTRL->DFLLCTRL.bit.ENABLE = 1; |
| |
| #if !CONFIG_SOC_ATMEL_SAML_OPENLOOP_AS_MAIN |
| /* wait for ready... note in open loop mode, we won't get a lock */ |
| while (!OSCCTRL->STATUS.bit.DFLLLCKC || !OSCCTRL->STATUS.bit.DFLLLCKF) { |
| } |
| #endif |
| } |
| |
| static inline void flash_waitstates_init(void) |
| { |
| /* PL2, >= 2.7v, 48MHz = 2 wait states */ |
| NVMCTRL->CTRLB.bit.RWS = 2; |
| } |
| |
| static inline void pm_init(void) |
| { |
| PM->PLCFG.bit.PLDIS = 0; |
| PM->PLCFG.bit.PLSEL = 2; |
| } |
| |
| static inline void gclk_main_configure(void) |
| { |
| /* finally, switch the CPU over to run from DFLL48M */ |
| GCLK->GENCTRL[0].bit.SRC = GCLK_GENCTRL_SRC_DFLL48M_Val; |
| } |
| |
| #if !CONFIG_ADC_SAM0 |
| #define gclk_adc_configure() |
| #else |
| static inline void gclk_adc_configure(void) |
| { |
| GCLK->GENCTRL[3].reg = 0 |
| | GCLK_GENCTRL_SRC_DFLL48M |
| | GCLK_GENCTRL_DIV(2) |
| | GCLK_GENCTRL_GENEN; |
| } |
| #endif |
| |
| #if CONFIG_SOC_ATMEL_SAML_DEBUG_PAUSE |
| static inline void pause_for_debug(void) |
| { |
| /* for some reason, when attempting to flash / debug the target, the operations |
| * will time out... I suspect this is due to clock configuration, so instead of |
| * doing this immediately, we defer startup for a while to permit the debugger |
| * to jump in and interrupt us. ick |
| */ |
| for (uint32_t i = 0; i < 10000; i += 1) { |
| __asm__ volatile ("nop\n"); |
| } |
| } |
| #else |
| static inline void pause_for_debug(void) {} |
| #endif |
| |
| static int atmel_saml_init(void) |
| { |
| uint32_t key; |
| |
| |
| key = irq_lock(); |
| |
| pause_for_debug(); |
| |
| gclk_reset(); |
| osc32k_init(); |
| xosc32k_init(); |
| osc16m_init(); |
| dfll48m_init(); |
| flash_waitstates_init(); |
| pm_init(); |
| gclk_main_configure(); |
| gclk_adc_configure(); |
| |
| /* 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_saml_init, PRE_KERNEL_1, 0); |