blob: ee77f0f5f018156f761a5985bbc86abda53acc2e [file] [log] [blame]
/*
* Copyright (c) 2018 Sean Nyekjaer
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Atmel SAMD MCU series initialization code
*/
#include <arch/cpu.h>
#include <cortex_m/exc.h>
#include <device.h>
#include <init.h>
#include <kernel.h>
#include <soc.h>
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)
{
u32_t fuse = *(u32_t *)FUSES_OSC32KCAL_ADDR;
u32_t calib = (fuse & FUSES_OSC32KCAL_Msk) >> FUSES_OSC32KCAL_Pos;
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) {
}
u32_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 |
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(struct device *arg)
{
u32_t key;
ARG_UNUSED(arg);
key = irq_lock();
z_clearfaults();
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);