From 5c6aea9fd8a26cb4c1f9b41a93114f0f8ab529a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Fri, 1 Jun 2018 14:06:43 +0200 Subject: [PATCH 1/3] atmel-samd/samd51: Implement samd.clock Fill out the dummy implementation. --- ports/atmel-samd/samd51_clocks.c | 323 +++++++++++++++++++++++++++++++ 1 file changed, 323 insertions(+) diff --git a/ports/atmel-samd/samd51_clocks.c b/ports/atmel-samd/samd51_clocks.c index cc3dd7917a..d5cb8aed03 100644 --- a/ports/atmel-samd/samd51_clocks.c +++ b/ports/atmel-samd/samd51_clocks.c @@ -28,6 +28,7 @@ #include "hpl_gclk_config.h" +#include "bindings/samd/Clock.h" #include "shared-bindings/microcontroller/__init__.h" #include "py/runtime.h" @@ -61,26 +62,348 @@ void disable_clock_generator(uint8_t gclk) { while ((GCLK->SYNCBUSY.vec.GENCTRL & (1 << gclk)) != 0) {} } +static bool clk_enabled(uint8_t clk) { + return GCLK->PCHCTRL[clk].bit.CHEN; +} + +static uint8_t clk_get_generator(uint8_t clk) { + return GCLK->PCHCTRL[clk].bit.GEN; +} + +static uint8_t generator_get_source(uint8_t gen) { + return GCLK->GENCTRL[gen].bit.SRC; +} + +static bool osc_enabled(uint8_t index) { + switch (index) { + case GCLK_SOURCE_XOSC0: + return OSCCTRL->XOSCCTRL[0].bit.ENABLE; + case GCLK_SOURCE_XOSC1: + return OSCCTRL->XOSCCTRL[1].bit.ENABLE; + case GCLK_SOURCE_OSCULP32K: + return true; + case GCLK_SOURCE_XOSC32K: + return OSC32KCTRL->XOSC32K.bit.ENABLE; + case GCLK_SOURCE_DFLL: + return OSCCTRL->DFLLCTRLA.bit.ENABLE; + case GCLK_SOURCE_DPLL0: + return OSCCTRL->Dpll[0].DPLLCTRLA.bit.ENABLE; + case GCLK_SOURCE_DPLL1: + return OSCCTRL->Dpll[1].DPLLCTRLA.bit.ENABLE; + }; + return false; +} + +static uint32_t osc_get_source(uint8_t index) { + uint8_t dpll_index = index - GCLK_SOURCE_DPLL0; + uint32_t refclk = OSCCTRL->Dpll[dpll_index].DPLLCTRLB.bit.REFCLK; + switch (refclk) { + case 0x0: + return generator_get_source(GCLK->PCHCTRL[OSCCTRL_GCLK_ID_FDPLL0 + dpll_index].bit.GEN); + case 0x1: + return GCLK_SOURCE_XOSC32K; + case 0x2: + return GCLK_SOURCE_XOSC0; + case 0x3: + return GCLK_SOURCE_XOSC1; + } + return 0; +} + +static uint32_t osc_get_frequency(uint8_t index); + +static uint32_t generator_get_frequency(uint8_t gen) { + uint8_t src = GCLK->GENCTRL[gen].bit.SRC; + uint32_t div; + if (GCLK->GENCTRL[gen].bit.DIVSEL) { + div = 1 << (GCLK->GENCTRL[gen].bit.DIV + 1); + } else { + div = GCLK->GENCTRL[gen].bit.DIV; + if (!div) + div = 1; + } + + return osc_get_frequency(src) / div; +} + +static uint32_t dpll_get_frequency(uint8_t index) { + uint8_t dpll_index = index - GCLK_SOURCE_DPLL0; + uint32_t refclk = OSCCTRL->Dpll[dpll_index].DPLLCTRLB.bit.REFCLK; + uint32_t freq; + + switch (refclk) { + case 0x0: // GCLK + freq = generator_get_frequency(GCLK->PCHCTRL[OSCCTRL_GCLK_ID_FDPLL0 + dpll_index].bit.GEN); + break; + case 0x1: // XOSC32 + freq = 32768; + break; + case 0x2: // XOSC0 + case 0x3: // XOSC1 + return 0; // unknown + } + + return (freq * (OSCCTRL->Dpll[dpll_index].DPLLRATIO.bit.LDR + 1)) + + (freq * OSCCTRL->Dpll[dpll_index].DPLLRATIO.bit.LDRFRAC / 32); +} + +static uint32_t osc_get_frequency(uint8_t index) { + switch (index) { + case GCLK_SOURCE_XOSC0: + case GCLK_SOURCE_XOSC1: + return 0; // unknown + case GCLK_SOURCE_OSCULP32K: + case GCLK_SOURCE_XOSC32K: + return 32768; + case GCLK_SOURCE_DFLL: + return 48000000; + case GCLK_SOURCE_DPLL0: + case GCLK_SOURCE_DPLL1: + return dpll_get_frequency(index); + } + return 0; +} + bool clock_get_enabled(uint8_t type, uint8_t index) { + if (type == 0) + return osc_enabled(index); + if (type == 1) + return clk_enabled(index); + if (type == 2) + return SysTick->CTRL & SysTick_CTRL_ENABLE_Msk; return false; } bool clock_get_parent(uint8_t type, uint8_t index, uint8_t *p_type, uint8_t *p_index) { + if (type == 0 && osc_enabled(index)) { + if (index == GCLK_SOURCE_DPLL0 || index == GCLK_SOURCE_DPLL1) { + *p_type = 0; + *p_index = osc_get_source(index); + return true; + } + return false; + } + if (type == 1 && index <= 47 && clk_enabled(index)) { + *p_type = 0; + *p_index = generator_get_source(clk_get_generator(index)); + return true; + } + if (type == 2) { + switch (index) { + case 0: + case 1: + *p_type = 0; + *p_index = generator_get_source(0); + return true; + case 2: + *p_type = 0; + switch (OSC32KCTRL->RTCCTRL.bit.RTCSEL) { + case 0: + case 1: + *p_index = GCLK_SOURCE_OSCULP32K; + return true; + case 4: + case 5: + *p_index = GCLK_SOURCE_XOSC32K; + return true; + } + return false; + } + } return false; } uint32_t clock_get_frequency(uint8_t type, uint8_t index) { + if (type == 0) { + return osc_get_frequency(index); + } + if (type == 1 && index <= 47 && clk_enabled(index)) { + return generator_get_frequency(clk_get_generator(index)); + } + if (type == 2) { + switch (index) { + case 0: + return clock_get_frequency(0, generator_get_source(0)) / SysTick->LOAD; + case 1: + return clock_get_frequency(0, generator_get_source(0)) / MCLK->CPUDIV.bit.DIV; + case 2: + switch (OSC32KCTRL->RTCCTRL.bit.RTCSEL) { + case 0: + case 4: + return 1024; + case 1: + case 5: + return 32768; + } + } + } return 0; } uint32_t clock_get_calibration(uint8_t type, uint8_t index) { + if (type == 0) { + switch (index) { + case GCLK_SOURCE_OSCULP32K: + return OSC32KCTRL->OSCULP32K.bit.CALIB; + }; + } + if (type == 2 && index == 0) { + return SysTick->LOAD + 1; + } return 0; } int clock_set_calibration(uint8_t type, uint8_t index, uint32_t val) { + if (type == 0) { + switch (index) { + case GCLK_SOURCE_OSCULP32K: + if (val > 0x3f) + return -1; + OSC32KCTRL->OSCULP32K.bit.CALIB = val; + return 0; + }; + } + if (type == 2 && index == 0) { + if (val < 0x1000 || val > 0x1000000) + return -1; + SysTick->LOAD = val - 1; + return 0; + } return -2; } +#include +#include +#include +#include +#include +#include +#include + +CLOCK_SOURCE(XOSC0); +CLOCK_SOURCE(XOSC1); +CLOCK_SOURCE(GCLKIN); +CLOCK_SOURCE(GCLKGEN1); +CLOCK_SOURCE(OSCULP32K); +CLOCK_SOURCE(XOSC32K); +CLOCK_SOURCE(DFLL); +CLOCK_SOURCE(DPLL0); +CLOCK_SOURCE(DPLL1); + +CLOCK_GCLK_(OSCCTRL, DFLL48); +CLOCK_GCLK_(OSCCTRL, FDPLL0); +CLOCK_GCLK_(OSCCTRL, FDPLL1); +CLOCK_GCLK_(OSCCTRL, FDPLL032K); // GCLK_OSCCTRL_FDPLL1_32K, GCLK_SDHC0_SLOW, GCLK_SDHC1_SLOW, GCLK_SERCOM[0..7]_SLOW +CLOCK_GCLK(EIC); +CLOCK_GCLK_(FREQM, MSR); +// 6: GCLK_FREQM_REF +CLOCK_GCLK_(SERCOM0, CORE); +CLOCK_GCLK_(SERCOM1, CORE); +CLOCK(TC0_TC1, 1, 9); +CLOCK_GCLK(USB); +CLOCK_GCLK_(EVSYS, 0); +CLOCK_GCLK_(EVSYS, 1); +CLOCK_GCLK_(EVSYS, 2); +CLOCK_GCLK_(EVSYS, 3); +CLOCK_GCLK_(EVSYS, 4); +CLOCK_GCLK_(EVSYS, 5); +CLOCK_GCLK_(EVSYS, 6); +CLOCK_GCLK_(EVSYS, 7); +CLOCK_GCLK_(EVSYS, 8); +CLOCK_GCLK_(EVSYS, 9); +CLOCK_GCLK_(EVSYS, 10); +CLOCK_GCLK_(EVSYS, 11); +CLOCK_GCLK_(SERCOM2, CORE); +CLOCK_GCLK_(SERCOM3, CORE); +CLOCK(TCC0_TCC1, 1, 25); +CLOCK(TC2_TC3, 1, 26); +CLOCK_GCLK(CAN0); +CLOCK_GCLK(CAN1); +CLOCK(TCC2_TCC3, 1, 29); +CLOCK(TC4_TC5, 1, 30); +CLOCK_GCLK(PDEC); +CLOCK_GCLK(AC); +CLOCK_GCLK(CCL); +CLOCK_GCLK_(SERCOM4, CORE); +CLOCK_GCLK_(SERCOM5, CORE); +CLOCK_GCLK_(SERCOM6, CORE); +CLOCK_GCLK_(SERCOM7, CORE); +CLOCK_GCLK(TCC4); +CLOCK(TC6_TC7, 1, 39); +CLOCK_GCLK(ADC0); +CLOCK_GCLK(ADC1); +CLOCK_GCLK(DAC); +CLOCK_GCLK_(I2S, 0); +CLOCK_GCLK_(I2S, 1); +CLOCK_GCLK(SDHC0); +CLOCK_GCLK(SDHC1); +// 47: GCLK_CM4_TRACE + +CLOCK(SYSTICK, 2, 0); +CLOCK(CPU, 2, 1); +CLOCK(RTC, 2, 2); + + STATIC const mp_rom_map_elem_t samd_clock_global_dict_table[] = { + CLOCK_ENTRY(XOSC0), + CLOCK_ENTRY(XOSC1), + CLOCK_ENTRY(GCLKIN), + CLOCK_ENTRY(GCLKGEN1), + CLOCK_ENTRY(OSCULP32K), + CLOCK_ENTRY(XOSC32K), + CLOCK_ENTRY(DFLL), + CLOCK_ENTRY(DPLL0), + CLOCK_ENTRY(DPLL1), + + CLOCK_ENTRY_(OSCCTRL, DFLL48), + CLOCK_ENTRY_(OSCCTRL, FDPLL0), + CLOCK_ENTRY_(OSCCTRL, FDPLL1), + CLOCK_ENTRY_(OSCCTRL, FDPLL032K), + CLOCK_ENTRY(EIC), + CLOCK_ENTRY_(FREQM, MSR), + CLOCK_ENTRY_(SERCOM0, CORE), + CLOCK_ENTRY_(SERCOM1, CORE), + CLOCK_ENTRY(TC0_TC1), + CLOCK_ENTRY(USB), + CLOCK_ENTRY_(EVSYS, 0), + CLOCK_ENTRY_(EVSYS, 1), + CLOCK_ENTRY_(EVSYS, 2), + CLOCK_ENTRY_(EVSYS, 3), + CLOCK_ENTRY_(EVSYS, 4), + CLOCK_ENTRY_(EVSYS, 5), + CLOCK_ENTRY_(EVSYS, 6), + CLOCK_ENTRY_(EVSYS, 7), + CLOCK_ENTRY_(EVSYS, 8), + CLOCK_ENTRY_(EVSYS, 9), + CLOCK_ENTRY_(EVSYS, 10), + CLOCK_ENTRY_(EVSYS, 11), + CLOCK_ENTRY_(SERCOM2, CORE), + CLOCK_ENTRY_(SERCOM3, CORE), + CLOCK_ENTRY(TCC0_TCC1), + CLOCK_ENTRY(TC2_TC3), + CLOCK_ENTRY(CAN0), + CLOCK_ENTRY(CAN1), + CLOCK_ENTRY(TCC2_TCC3), + CLOCK_ENTRY(TC4_TC5), + CLOCK_ENTRY(PDEC), + CLOCK_ENTRY(AC), + CLOCK_ENTRY(CCL), + CLOCK_ENTRY_(SERCOM4, CORE), + CLOCK_ENTRY_(SERCOM5, CORE), + CLOCK_ENTRY_(SERCOM6, CORE), + CLOCK_ENTRY_(SERCOM7, CORE), + CLOCK_ENTRY(TCC4), + CLOCK_ENTRY(TC6_TC7), + CLOCK_ENTRY(ADC0), + CLOCK_ENTRY(ADC1), + CLOCK_ENTRY(DAC), + CLOCK_ENTRY_(I2S, 0), + CLOCK_ENTRY_(I2S, 1), + CLOCK_ENTRY(SDHC0), + CLOCK_ENTRY(SDHC1), + + CLOCK_ENTRY(SYSTICK), + CLOCK_ENTRY(CPU), + CLOCK_ENTRY(RTC), }; MP_DEFINE_CONST_DICT(samd_clock_globals, samd_clock_global_dict_table); From ab7ddfddd53173e03c25176d7d1665504a8a8f5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Fri, 1 Jun 2018 15:33:25 +0200 Subject: [PATCH 2/3] atmel-samd/samd51: Refactor clock setup Refactor the convoluted asf4 clock setup into something more readable. enable_clock_generator() has 2 changes: - Set 'Output enabled' to match the current clock setup - Handle divisors above 511 Add an enable_clock_generator_sync() version which makes it possible to setup clocks without waiting for syncing. The bootup would hang without this. I have checked these registers: NVMCTRL->CTRLA = 0x00000004 Peripheral clocks (only non-zero shown): PCHCTRL[1]=0x00000045 PCHCTRL[10]=0x00000041 Generator clocks (only non-zero shown): GENCTRL[0] = 0x00010907 GENCTRL[1] = 0x00010906 -GENCTRL[2] = 0x00041104 +GENCTRL[2] = 0x00200904 GENCTRL[4] = 0x00010907 GENCTRL[5] = 0x00180906 DFLL clock: OSCCTRL->DFLLCTRLA = 0x00000082 OSCCTRL->DFLLCTRLB = 0x00000000 OSCCTRL->DFLLVAL = 0x00008082 OSCCTRL->DFLLMUL = 0x00000000 DPLL clocks: OSCCTRL->Dpll[0].DPLLCTRLA=0x00000002 OSCCTRL->Dpll[0].DPLLCTRLB=0x00000000 OSCCTRL->Dpll[0].DPLLRATIO=0x0000003b OSCCTRL->Dpll[1].DPLLCTRLA=0x00000080 OSCCTRL->Dpll[1].DPLLCTRLB=0x00000020 OSCCTRL->Dpll[1].DPLLRATIO=0x00000000 OSC32KCTRL clock: OSC32KCTRL->RTCCTRL = 0x00000000 OSC32KCTRL->XOSC32K = 0x00002082 OSC32KCTRL->CFDCTRL = 0x00000000 OSC32KCTRL->EVCTRL = 0x00000000 OSC32KCTRL->OSCULP32K = 0x00002300 Only gen2 changed which is due to samd51 having more bits in the simple division register so DIVSEL wasn't necessary, and it didn't have OE set. --- ports/atmel-samd/samd51_clocks.c | 62 +++++++++++++++++++++++++++++- ports/atmel-samd/supervisor/port.c | 6 +-- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/ports/atmel-samd/samd51_clocks.c b/ports/atmel-samd/samd51_clocks.c index d5cb8aed03..7424636a40 100644 --- a/ports/atmel-samd/samd51_clocks.c +++ b/ports/atmel-samd/samd51_clocks.c @@ -52,9 +52,26 @@ void disconnect_gclk_from_peripheral(uint8_t gclk, uint8_t peripheral) { GCLK->PCHCTRL[peripheral].reg = 0; } +static void enable_clock_generator_sync(uint8_t gclk, uint32_t source, uint16_t divisor, bool sync) { + uint32_t divsel = 0; + // The datasheet says 8 bits and max value of 512, how is that possible? + if (divisor > 255) { // Generator 1 has 16 bits + divsel = GCLK_GENCTRL_DIVSEL; + for (int i = 15; i > 0; i--) { + if (divisor & (1 << i)) { + divisor = i - 1; + break; + } + } + } + + GCLK->GENCTRL[gclk].reg = GCLK_GENCTRL_SRC(source) | GCLK_GENCTRL_DIV(divisor) | divsel | GCLK_GENCTRL_OE | GCLK_GENCTRL_GENEN; + if (sync) + while ((GCLK->SYNCBUSY.vec.GENCTRL & (1 << gclk)) != 0) {} +} + void enable_clock_generator(uint8_t gclk, uint32_t source, uint16_t divisor) { - GCLK->GENCTRL[gclk].reg = GCLK_GENCTRL_SRC(source) | GCLK_GENCTRL_DIV(divisor) | GCLK_GENCTRL_GENEN; - while ((GCLK->SYNCBUSY.vec.GENCTRL & (1 << gclk)) != 0) {} + enable_clock_generator_sync(gclk, source, divisor, true); } void disable_clock_generator(uint8_t gclk) { @@ -62,6 +79,47 @@ void disable_clock_generator(uint8_t gclk) { while ((GCLK->SYNCBUSY.vec.GENCTRL & (1 << gclk)) != 0) {} } +static void init_clock_source_osculp32k(void) { + // Calibration value is loaded at startup + OSC32KCTRL->OSCULP32K.bit.EN1K = 0; + OSC32KCTRL->OSCULP32K.bit.EN32K = 0; +} + +static void init_clock_source_xosc32k(void) { + OSC32KCTRL->XOSC32K.reg = OSC32KCTRL_XOSC32K_ONDEMAND | + OSC32KCTRL_XOSC32K_ENABLE | + OSC32KCTRL_XOSC32K_CGM(1); +} + +static void init_clock_source_dpll0(void) +{ + GCLK->PCHCTRL[OSCCTRL_GCLK_ID_FDPLL0].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(5); + OSCCTRL->Dpll[0].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDRFRAC(0) | OSCCTRL_DPLLRATIO_LDR(59); + OSCCTRL->Dpll[0].DPLLCTRLB.reg = OSCCTRL_DPLLCTRLB_REFCLK(0); + OSCCTRL->Dpll[0].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_ENABLE; + + while (!(OSCCTRL->Dpll[0].DPLLSTATUS.bit.LOCK || OSCCTRL->Dpll[0].DPLLSTATUS.bit.CLKRDY)) {} +} + +void clock_init(void) { + // DFLL48M is enabled by default + + init_clock_source_osculp32k(); + init_clock_source_xosc32k(); + + OSC32KCTRL->RTCCTRL.bit.RTCSEL = OSC32KCTRL_RTCCTRL_RTCSEL_ULP1K_Val; + + MCLK->CPUDIV.reg = MCLK_CPUDIV_DIV(1); + + enable_clock_generator_sync(0, GCLK_GENCTRL_SRC_DPLL0_Val, 1, false); + enable_clock_generator_sync(1, GCLK_GENCTRL_SRC_DFLL_Val, 1, false); + enable_clock_generator_sync(2, GCLK_GENCTRL_SRC_OSCULP32K_Val, 32, false); + enable_clock_generator_sync(4, GCLK_GENCTRL_SRC_DPLL0_Val, 1, false); + enable_clock_generator_sync(5, GCLK_GENCTRL_SRC_DFLL_Val, 24, false); + + init_clock_source_dpll0(); +} + static bool clk_enabled(uint8_t clk) { return GCLK->PCHCTRL[clk].bit.CHEN; } diff --git a/ports/atmel-samd/supervisor/port.c b/ports/atmel-samd/supervisor/port.c index a92ab2913f..55304e8bab 100644 --- a/ports/atmel-samd/supervisor/port.c +++ b/ports/atmel-samd/supervisor/port.c @@ -181,11 +181,9 @@ safe_mode_t port_init(void) { #ifdef SAMD21 hri_nvmctrl_set_CTRLB_RWS_bf(NVMCTRL, 2); _pm_init(); +#endif clock_init(); -#endif -#ifdef SAMD51 - init_mcu(); -#endif + board_init(); // Configure millisecond timer initialization. From e158702a68637a975c97c34cc0a564c4d693faa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Fri, 1 Jun 2018 16:12:48 +0200 Subject: [PATCH 3/3] atmel-samd/samd51: Use crystal for RTC This uses the crystal to clock the RTC on boards which have a crystal. Disable clock generator 2 which was enabled in commit 8e2080411f65 ("atmel-samd: Add rtc module support"). samd51 differs from samd21 when it comes to the RTC clock. samd51 doesn't have an explicit clock peripheral so no need for a clock generator. The same commit didn't even setup XOSC32K correctly, it missed EN1K and XTALEN. The RTC uses the 1k clock output, so enable it on the OSCULP32K even if it works without it. --- .../boards/feather_m4_express/mpconfigboard.h | 2 ++ .../boards/metro_m4_express/mpconfigboard.h | 2 ++ ports/atmel-samd/samd51_clocks.c | 13 +++++++++---- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.h b/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.h index 64b22acd57..b5af61ba6f 100644 --- a/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.h @@ -32,6 +32,8 @@ #include "external_flash/external_flash.h" +#define BOARD_HAS_CRYSTAL 1 + #define DEFAULT_I2C_BUS_SCL (&pin_PA13) #define DEFAULT_I2C_BUS_SDA (&pin_PA12) diff --git a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h index cda43d95e6..7aef929f7c 100644 --- a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h @@ -33,6 +33,8 @@ #include "external_flash/external_flash.h" +#define BOARD_HAS_CRYSTAL 1 + #define DEFAULT_I2C_BUS_SCL (&pin_PB03) #define DEFAULT_I2C_BUS_SDA (&pin_PB02) diff --git a/ports/atmel-samd/samd51_clocks.c b/ports/atmel-samd/samd51_clocks.c index 7424636a40..6f8b1f0102 100644 --- a/ports/atmel-samd/samd51_clocks.c +++ b/ports/atmel-samd/samd51_clocks.c @@ -81,12 +81,14 @@ void disable_clock_generator(uint8_t gclk) { static void init_clock_source_osculp32k(void) { // Calibration value is loaded at startup - OSC32KCTRL->OSCULP32K.bit.EN1K = 0; + OSC32KCTRL->OSCULP32K.bit.EN1K = 1; OSC32KCTRL->OSCULP32K.bit.EN32K = 0; } static void init_clock_source_xosc32k(void) { OSC32KCTRL->XOSC32K.reg = OSC32KCTRL_XOSC32K_ONDEMAND | + OSC32KCTRL_XOSC32K_EN1K | + OSC32KCTRL_XOSC32K_XTALEN | OSC32KCTRL_XOSC32K_ENABLE | OSC32KCTRL_XOSC32K_CGM(1); } @@ -105,15 +107,18 @@ void clock_init(void) { // DFLL48M is enabled by default init_clock_source_osculp32k(); - init_clock_source_xosc32k(); - OSC32KCTRL->RTCCTRL.bit.RTCSEL = OSC32KCTRL_RTCCTRL_RTCSEL_ULP1K_Val; + if (board_has_crystal()) { + init_clock_source_xosc32k(); + OSC32KCTRL->RTCCTRL.bit.RTCSEL = OSC32KCTRL_RTCCTRL_RTCSEL_XOSC1K_Val; + } else { + OSC32KCTRL->RTCCTRL.bit.RTCSEL = OSC32KCTRL_RTCCTRL_RTCSEL_ULP1K_Val; + } MCLK->CPUDIV.reg = MCLK_CPUDIV_DIV(1); enable_clock_generator_sync(0, GCLK_GENCTRL_SRC_DPLL0_Val, 1, false); enable_clock_generator_sync(1, GCLK_GENCTRL_SRC_DFLL_Val, 1, false); - enable_clock_generator_sync(2, GCLK_GENCTRL_SRC_OSCULP32K_Val, 32, false); enable_clock_generator_sync(4, GCLK_GENCTRL_SRC_DPLL0_Val, 1, false); enable_clock_generator_sync(5, GCLK_GENCTRL_SRC_DFLL_Val, 24, false);