From 7e0b1bc95dffad4056ab6a1d6bc54f51cb6a6002 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 24 Jan 2023 18:37:27 +0100 Subject: [PATCH] samd/mcu: Use the FDPLL96M clock for the SAMD21 CPU. Allowing to increase the clock a little bit to 54Mhz. Not much of a gain, but useful for generating a RNG entropy source from the jitter between DFLL48M and FDPLL96M. --- ports/samd/mcu/samd21/clock_config.c | 48 ++++++++++++++++++++++------ ports/samd/mcu/samd21/mpconfigmcu.h | 3 +- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/ports/samd/mcu/samd21/clock_config.c b/ports/samd/mcu/samd21/clock_config.c index ae8441b7ca..4473be5afc 100644 --- a/ports/samd/mcu/samd21/clock_config.c +++ b/ports/samd/mcu/samd21/clock_config.c @@ -55,10 +55,10 @@ void set_cpu_freq(uint32_t cpu_freq_arg) { // Set 1 waitstate to be safe NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_MANW | NVMCTRL_CTRLB_RWS(1); - int div = DFLL48M_FREQ / cpu_freq_arg; - peripheral_freq = cpu_freq = DFLL48M_FREQ / div; + int div = MAX(DFLL48M_FREQ / cpu_freq_arg, 1); + peripheral_freq = DFLL48M_FREQ / div; - // Enable GCLK output: 48M on both CCLK0 and GCLK2 + // Enable GCLK output: 48MHz from DFLL48M on both CCLK0 and GCLK2 GCLK->GENDIV.reg = GCLK_GENDIV_ID(0) | GCLK_GENDIV_DIV(div); GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_ID(0); while (GCLK->STATUS.bit.SYNCBUSY) { @@ -67,6 +67,32 @@ void set_cpu_freq(uint32_t cpu_freq_arg) { GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_ID(2); while (GCLK->STATUS.bit.SYNCBUSY) { } + // The comparison is >=, such that for 48MHz still the FDPLL96 is used for the CPU clock. + if (cpu_freq_arg >= 48000000) { + cpu_freq = cpu_freq_arg; + // Connect GCLK1 to the FDPLL96 input. + GCLK->CLKCTRL.reg = GCLK_CLKCTRL_GEN_GCLK1 | GCLK_CLKCTRL_ID_FDPLL | GCLK_CLKCTRL_CLKEN; + while (GCLK->STATUS.bit.SYNCBUSY) { + } + // configure the FDPLL96 + // CtrlB: Set the ref ource to GCLK, set the Wakup-Fast Flag. + SYSCTRL->DPLLCTRLB.reg = SYSCTRL_DPLLCTRLB_REFCLK_GCLK | SYSCTRL_DPLLCTRLB_WUF; + // Set the FDPLL ratio and enable the DPLL. + int ldr = cpu_freq_arg / FDPLL_REF_FREQ - 1; + SYSCTRL->DPLLRATIO.reg = SYSCTRL_DPLLRATIO_LDR(ldr); + SYSCTRL->DPLLCTRLA.reg = SYSCTRL_DPLLCTRLA_ENABLE; + // Wait for the DPLL lock. + while (!SYSCTRL->DPLLSTATUS.bit.LOCK) { + } + // Finally switch GCLK0 to FDPLL96M. + GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DPLL96M | GCLK_GENCTRL_ID(0); + while (GCLK->STATUS.bit.SYNCBUSY) { + } + } else { + cpu_freq = peripheral_freq; + // Disable the FDPLL96M in case it was enabled. + SYSCTRL->DPLLCTRLA.reg = 0; + } if (cpu_freq >= 8000000) { // Enable GCLK output: 48MHz on GCLK5 for USB GCLK->GENDIV.reg = GCLK_GENDIV_ID(5) | GCLK_GENDIV_DIV(1); @@ -136,8 +162,8 @@ void init_clocks(uint32_t cpu_freq) { // SAMD21 Clock settings // - // GCLK0: 48MHz, source: DFLL48M, usage: CPU - // GCLK1: 32kHz, source: XOSC32K or OSCULP32K or DFLL48M, usage: FDPLL96M reference + // GCLK0: 48MHz, source: DFLL48M or FDPLL96M, usage: CPU + // GCLK1: 32kHz, source: XOSC32K or OSCULP32K, usage: FDPLL96M reference // GCLK2: 1-48MHz, source: DFLL48M, usage: Peripherals // GCLK3: 1Mhz, source: DFLL48M, usage: us-counter (TC4/TC5) // GCLK4: 32kHz, source: XOSC32K, if crystal present, usage: DFLL48M reference @@ -145,8 +171,10 @@ void init_clocks(uint32_t cpu_freq) { // GCLK8: 1kHz, source: XOSC32K or OSCULP32K, usage: WDT and RTC // DFLL48M: Reference sources: // - in closed loop mode: eiter XOSC32K or OSCULP32K or USB clock + // from GCLK4. // - in open loop mode: None - // FDPLL96M: Not used (yet). Option to use it for the CPU clock. + // FDPLL96M: Reference source GCLK1 + // Used for the CPU clock for freq >= 48Mhz NVMCTRL->CTRLB.bit.MANW = 1; // errata "Spurious Writes" NVMCTRL->CTRLB.bit.RWS = 1; // 1 read wait state for 48MHz @@ -166,7 +194,7 @@ void init_clocks(uint32_t cpu_freq) { GCLK->GENDIV.reg = GCLK_GENDIV_ID(1) | GCLK_GENDIV_DIV(1); GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSCULP32K | GCLK_GENCTRL_ID(1); #else - // Connect the GCLK1 to OSC32K via GCLK1 to the DFLL input and for further use. + // Connect the GCLK1 to OSC32K GCLK->GENDIV.reg = GCLK_GENDIV_ID(1) | GCLK_GENDIV_DIV(1); GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_XOSC32K | GCLK_GENCTRL_ID(1); #endif @@ -247,9 +275,9 @@ void init_clocks(uint32_t cpu_freq) { while (!SYSCTRL->PCLKSR.bit.DFLLRDY) { } - // Enable 32768 Hz on GCLK1 for consistency - GCLK->GENDIV.reg = GCLK_GENDIV_ID(1) | GCLK_GENDIV_DIV(48016384 / 32768); - GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_ID(1); + // Connect the GCLK1 to the XOSC32KULP + GCLK->GENDIV.reg = GCLK_GENDIV_ID(1) | GCLK_GENDIV_DIV(1); + GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSCULP32K | GCLK_GENCTRL_ID(1); while (GCLK->STATUS.bit.SYNCBUSY) { } // Set GCLK8 to 1 kHz. diff --git a/ports/samd/mcu/samd21/mpconfigmcu.h b/ports/samd/mcu/samd21/mpconfigmcu.h index fd6f524d7f..7d787cacfe 100644 --- a/ports/samd/mcu/samd21/mpconfigmcu.h +++ b/ports/samd/mcu/samd21/mpconfigmcu.h @@ -40,7 +40,8 @@ #define CPU_FREQ (48000000) #define DFLL48M_FREQ (48000000) -#define MAX_CPU_FREQ (48000000) +#define MAX_CPU_FREQ (54000000) +#define FDPLL_REF_FREQ (32768) #define IRQ_PRI_PENDSV ((1 << __NVIC_PRIO_BITS) - 1)