Skip to content

Commit

Permalink
samd/mcu: Use the FDPLL96M clock for the SAMD21 CPU.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
robert-hh authored and dpgeorge committed Feb 21, 2023
1 parent 60ab556 commit 7e0b1bc
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 11 deletions.
48 changes: 38 additions & 10 deletions ports/samd/mcu/samd21/clock_config.c
Expand Up @@ -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) {
Expand All @@ -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);
Expand Down Expand Up @@ -136,17 +162,19 @@ 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
// GCLK5: 48MHz, source: DFLL48M, usage: USB
// 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
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion ports/samd/mcu/samd21/mpconfigmcu.h
Expand Up @@ -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)

Expand Down

0 comments on commit 7e0b1bc

Please sign in to comment.