blob: 90c0d1b008d2fee8380f82a3cd51895d4ff03c6d [file] [log] [blame]
/*
* Copyright (c) 2017 Google LLC.
* Copyright (c) 2023 Ionut Catalin Pavel <iocapa@iocapa.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Atmel SAMD MCU series initialization code
*/
/* The CPU clock will be configured to the DT requested value,
* and run via DFLL48M.
*
* Reference -> GCLK Gen 1 -> DFLL48M -> GCLK Gen 0 -> GCLK_MAIN
*
* GCLK Gen 0 -> GCLK_MAIN
* GCLK Gen 1 -> DFLL48M (variable)
* GCLK Gen 2 -> WDT @ 32768 Hz
* GCLK Gen 3 -> ADC @ 8 MHz
*/
#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>
/**
* Fix different naming conventions for SAMD20
*/
#ifdef FUSES_OSC32KCAL_ADDR
#define FUSES_OSC32K_CAL_ADDR FUSES_OSC32KCAL_ADDR
#define FUSES_OSC32K_CAL_Pos FUSES_OSC32KCAL_Pos
#define FUSES_OSC32K_CAL_Msk FUSES_OSC32KCAL_Msk
#endif
#if !CONFIG_SOC_ATMEL_SAMD_OSC8M || CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN
#define osc8m_init()
#else
static inline void osc8m_init(void)
{
uint32_t reg;
/* Save calibration */
reg = SYSCTRL->OSC8M.reg
& (SYSCTRL_OSC8M_FRANGE_Msk | SYSCTRL_OSC8M_CALIB_Msk);
SYSCTRL->OSC8M.reg = reg
| SYSCTRL_OSC8M_RUNSTDBY
| SYSCTRL_OSC8M_PRESC(0) /* 8MHz (/1) */
| SYSCTRL_OSC8M_ENABLE;
while (!SYSCTRL->PCLKSR.bit.OSC8MRDY) {
}
}
#endif
#if !CONFIG_SOC_ATMEL_SAMD_OSC32K || CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN
#define osc32k_init()
#else
static inline void osc32k_init(void)
{
uint32_t cal;
/* Get calibration value */
cal = (*((uint32_t *)FUSES_OSC32K_CAL_ADDR)
& FUSES_OSC32K_CAL_Msk) >> FUSES_OSC32K_CAL_Pos;
SYSCTRL->OSC32K.reg = SYSCTRL_OSC32K_CALIB(cal)
| SYSCTRL_OSC32K_STARTUP(0x5) /* 34 cycles / ~1ms */
| SYSCTRL_OSC32K_RUNSTDBY
| SYSCTRL_OSC32K_EN32K
| SYSCTRL_OSC32K_ENABLE;
while (!SYSCTRL->PCLKSR.bit.OSC32KRDY) {
}
}
#endif
#if !CONFIG_SOC_ATMEL_SAMD_XOSC || CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN
#define xosc_init()
#else
static inline void xosc_init(void)
{
SYSCTRL->XOSC.reg = SYSCTRL_XOSC_STARTUP(0x5) /* 32 cycles / ~1ms */
| SYSCTRL_XOSC_RUNSTDBY
| SYSCTRL_XOSC_AMPGC
#if CONFIG_SOC_ATMEL_SAMD_XOSC_FREQ_HZ <= 2000000
| SYSCTRL_XOSC_GAIN(0x0)
#elif CONFIG_SOC_ATMEL_SAMD_XOSC_FREQ_HZ <= 4000000
| SYSCTRL_XOSC_GAIN(0x1)
#elif CONFIG_SOC_ATMEL_SAMD_XOSC_FREQ_HZ <= 8000000
| SYSCTRL_XOSC_GAIN(0x2)
#elif CONFIG_SOC_ATMEL_SAMD_XOSC_FREQ_HZ <= 16000000
| SYSCTRL_XOSC_GAIN(0x3)
#elif CONFIG_SOC_ATMEL_SAMD_XOSC_FREQ_HZ <= 32000000
| SYSCTRL_XOSC_GAIN(0x4)
#endif
#if CONFIG_SOC_ATMEL_SAMD_XOSC_CRYSTAL
| SYSCTRL_XOSC_XTALEN
#endif
| SYSCTRL_XOSC_ENABLE;
while (!SYSCTRL->PCLKSR.bit.XOSCRDY) {
}
}
#endif
#if !CONFIG_SOC_ATMEL_SAMD_XOSC32K || CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN
#define xosc32k_init()
#else
static inline void xosc32k_init(void)
{
SYSCTRL->XOSC32K.reg = SYSCTRL_XOSC32K_STARTUP(0x1) /* 4096 cycles / ~0.13s */
| SYSCTRL_XOSC32K_RUNSTDBY
| SYSCTRL_XOSC32K_EN32K
| SYSCTRL_XOSC32K_AAMPEN
#if CONFIG_SOC_ATMEL_SAMD_XOSC32K_CRYSTAL
| SYSCTRL_XOSC32K_XTALEN
#endif
| SYSCTRL_XOSC32K_ENABLE;
while (!SYSCTRL->PCLKSR.bit.XOSC32KRDY) {
}
}
#endif
#if CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN
#define dfll48m_init()
#else
static inline void dfll48m_init(void)
{
uint32_t fcal, ccal;
GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(1)
#if CONFIG_SOC_ATMEL_SAMD_OSC32K_AS_MAIN
| GCLK_GENCTRL_SRC_OSC32K
#elif CONFIG_SOC_ATMEL_SAMD_XOSC32K_AS_MAIN
| GCLK_GENCTRL_SRC_XOSC32K
#elif CONFIG_SOC_ATMEL_SAMD_OSC8M_AS_MAIN
| GCLK_GENCTRL_SRC_OSC8M
#elif CONFIG_SOC_ATMEL_SAMD_XOSC_AS_MAIN
| GCLK_GENCTRL_SRC_XOSC
#endif
| GCLK_GENCTRL_IDC
| GCLK_GENCTRL_RUNSTDBY
| GCLK_GENCTRL_GENEN;
while (GCLK->STATUS.bit.SYNCBUSY) {
}
GCLK->GENDIV.reg = GCLK_GENDIV_ID(1)
| GCLK_GENDIV_DIV(SOC_ATMEL_SAM0_GCLK1_DIV);
while (GCLK->STATUS.bit.SYNCBUSY) {
}
/* Route multiplexer 0 to DFLL48M */
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(0)
| GCLK_CLKCTRL_GEN_GCLK1
| GCLK_CLKCTRL_CLKEN;
SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_MODE
| SYSCTRL_DFLLCTRL_QLDIS
| SYSCTRL_DFLLCTRL_RUNSTDBY;
/* Get calibration values */
ccal = (*((uint32_t *)FUSES_DFLL48M_COARSE_CAL_ADDR)
& FUSES_DFLL48M_COARSE_CAL_Msk) >> FUSES_DFLL48M_COARSE_CAL_Pos;
fcal = (*((uint32_t *)FUSES_DFLL48M_FINE_CAL_ADDR)
& FUSES_DFLL48M_FINE_CAL_Msk) >> FUSES_DFLL48M_FINE_CAL_Pos;
SYSCTRL->DFLLVAL.reg = SYSCTRL_DFLLVAL_COARSE(ccal)
| SYSCTRL_DFLLVAL_FINE(fcal);
/* Use half of maximum for both */
SYSCTRL->DFLLMUL.reg = SYSCTRL_DFLLMUL_CSTEP(31)
| SYSCTRL_DFLLMUL_FSTEP(511)
| SYSCTRL_DFLLMUL_MUL(SOC_ATMEL_SAM0_DFLL48M_MUL);
/* Enable */
while (!SYSCTRL->PCLKSR.bit.DFLLRDY) {
}
SYSCTRL->DFLLCTRL.bit.ENABLE = 1;
/* Wait for synchronization. */
while (!SYSCTRL->PCLKSR.bit.DFLLLCKC || !SYSCTRL->PCLKSR.bit.DFLLLCKF) {
}
}
#endif
#if CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN
#define flash_waitstates_init()
#else
static inline void flash_waitstates_init(void)
{
NVMCTRL->CTRLB.bit.RWS = NVMCTRL_CTRLB_RWS(CONFIG_SOC_ATMEL_SAMD_NVM_WAIT_STATES);
}
#endif
#if CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN
#define gclk_main_configure()
#else
static inline void gclk_main_configure(void)
{
GCLK->GENDIV.reg = GCLK_GENDIV_ID(0)
| GCLK_GENDIV_DIV(SOC_ATMEL_SAM0_GCLK0_DIV);
while (GCLK->STATUS.bit.SYNCBUSY) {
}
GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(0)
| GCLK_GENCTRL_SRC_DFLL48M
| GCLK_GENCTRL_IDC
| GCLK_GENCTRL_GENEN;
while (GCLK->STATUS.bit.SYNCBUSY) {
}
}
#endif
#if !CONFIG_ADC_SAM0 || CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN
#define gclk_adc_configure()
#else
static inline void gclk_adc_configure(void)
{
GCLK->GENDIV.reg = GCLK_GENDIV_ID(3)
| GCLK_GENDIV_DIV(SOC_ATMEL_SAM0_GCLK3_DIV);
while (GCLK->STATUS.bit.SYNCBUSY) {
}
GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(3)
| GCLK_GENCTRL_SRC_DFLL48M
| GCLK_GENCTRL_IDC
| GCLK_GENCTRL_GENEN;
while (GCLK->STATUS.bit.SYNCBUSY) {
}
}
#endif
#if CONFIG_SOC_ATMEL_SAMD_OSC8M || CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN
#define osc8m_disable()
#else
static inline void osc8m_disable(void)
{
SYSCTRL->OSC8M.bit.ENABLE = 0;
}
#endif
static int atmel_samd_init(void)
{
uint32_t key;
key = irq_lock();
osc8m_init();
osc32k_init();
xosc_init();
xosc32k_init();
dfll48m_init();
flash_waitstates_init();
gclk_main_configure();
gclk_adc_configure();
osc8m_disable();
/* 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);