Skip to content

Commit 7e0b1bc

Browse files
robert-hhdpgeorge
authored andcommitted
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.
1 parent 60ab556 commit 7e0b1bc

File tree

2 files changed

+40
-11
lines changed

2 files changed

+40
-11
lines changed

Diff for: ports/samd/mcu/samd21/clock_config.c

+38-10
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ void set_cpu_freq(uint32_t cpu_freq_arg) {
5555
// Set 1 waitstate to be safe
5656
NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_MANW | NVMCTRL_CTRLB_RWS(1);
5757

58-
int div = DFLL48M_FREQ / cpu_freq_arg;
59-
peripheral_freq = cpu_freq = DFLL48M_FREQ / div;
58+
int div = MAX(DFLL48M_FREQ / cpu_freq_arg, 1);
59+
peripheral_freq = DFLL48M_FREQ / div;
6060

61-
// Enable GCLK output: 48M on both CCLK0 and GCLK2
61+
// Enable GCLK output: 48MHz from DFLL48M on both CCLK0 and GCLK2
6262
GCLK->GENDIV.reg = GCLK_GENDIV_ID(0) | GCLK_GENDIV_DIV(div);
6363
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_ID(0);
6464
while (GCLK->STATUS.bit.SYNCBUSY) {
@@ -67,6 +67,32 @@ void set_cpu_freq(uint32_t cpu_freq_arg) {
6767
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_ID(2);
6868
while (GCLK->STATUS.bit.SYNCBUSY) {
6969
}
70+
// The comparison is >=, such that for 48MHz still the FDPLL96 is used for the CPU clock.
71+
if (cpu_freq_arg >= 48000000) {
72+
cpu_freq = cpu_freq_arg;
73+
// Connect GCLK1 to the FDPLL96 input.
74+
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_GEN_GCLK1 | GCLK_CLKCTRL_ID_FDPLL | GCLK_CLKCTRL_CLKEN;
75+
while (GCLK->STATUS.bit.SYNCBUSY) {
76+
}
77+
// configure the FDPLL96
78+
// CtrlB: Set the ref ource to GCLK, set the Wakup-Fast Flag.
79+
SYSCTRL->DPLLCTRLB.reg = SYSCTRL_DPLLCTRLB_REFCLK_GCLK | SYSCTRL_DPLLCTRLB_WUF;
80+
// Set the FDPLL ratio and enable the DPLL.
81+
int ldr = cpu_freq_arg / FDPLL_REF_FREQ - 1;
82+
SYSCTRL->DPLLRATIO.reg = SYSCTRL_DPLLRATIO_LDR(ldr);
83+
SYSCTRL->DPLLCTRLA.reg = SYSCTRL_DPLLCTRLA_ENABLE;
84+
// Wait for the DPLL lock.
85+
while (!SYSCTRL->DPLLSTATUS.bit.LOCK) {
86+
}
87+
// Finally switch GCLK0 to FDPLL96M.
88+
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DPLL96M | GCLK_GENCTRL_ID(0);
89+
while (GCLK->STATUS.bit.SYNCBUSY) {
90+
}
91+
} else {
92+
cpu_freq = peripheral_freq;
93+
// Disable the FDPLL96M in case it was enabled.
94+
SYSCTRL->DPLLCTRLA.reg = 0;
95+
}
7096
if (cpu_freq >= 8000000) {
7197
// Enable GCLK output: 48MHz on GCLK5 for USB
7298
GCLK->GENDIV.reg = GCLK_GENDIV_ID(5) | GCLK_GENDIV_DIV(1);
@@ -136,17 +162,19 @@ void init_clocks(uint32_t cpu_freq) {
136162

137163
// SAMD21 Clock settings
138164
//
139-
// GCLK0: 48MHz, source: DFLL48M, usage: CPU
140-
// GCLK1: 32kHz, source: XOSC32K or OSCULP32K or DFLL48M, usage: FDPLL96M reference
165+
// GCLK0: 48MHz, source: DFLL48M or FDPLL96M, usage: CPU
166+
// GCLK1: 32kHz, source: XOSC32K or OSCULP32K, usage: FDPLL96M reference
141167
// GCLK2: 1-48MHz, source: DFLL48M, usage: Peripherals
142168
// GCLK3: 1Mhz, source: DFLL48M, usage: us-counter (TC4/TC5)
143169
// GCLK4: 32kHz, source: XOSC32K, if crystal present, usage: DFLL48M reference
144170
// GCLK5: 48MHz, source: DFLL48M, usage: USB
145171
// GCLK8: 1kHz, source: XOSC32K or OSCULP32K, usage: WDT and RTC
146172
// DFLL48M: Reference sources:
147173
// - in closed loop mode: eiter XOSC32K or OSCULP32K or USB clock
174+
// from GCLK4.
148175
// - in open loop mode: None
149-
// FDPLL96M: Not used (yet). Option to use it for the CPU clock.
176+
// FDPLL96M: Reference source GCLK1
177+
// Used for the CPU clock for freq >= 48Mhz
150178

151179
NVMCTRL->CTRLB.bit.MANW = 1; // errata "Spurious Writes"
152180
NVMCTRL->CTRLB.bit.RWS = 1; // 1 read wait state for 48MHz
@@ -166,7 +194,7 @@ void init_clocks(uint32_t cpu_freq) {
166194
GCLK->GENDIV.reg = GCLK_GENDIV_ID(1) | GCLK_GENDIV_DIV(1);
167195
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSCULP32K | GCLK_GENCTRL_ID(1);
168196
#else
169-
// Connect the GCLK1 to OSC32K via GCLK1 to the DFLL input and for further use.
197+
// Connect the GCLK1 to OSC32K
170198
GCLK->GENDIV.reg = GCLK_GENDIV_ID(1) | GCLK_GENDIV_DIV(1);
171199
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_XOSC32K | GCLK_GENCTRL_ID(1);
172200
#endif
@@ -247,9 +275,9 @@ void init_clocks(uint32_t cpu_freq) {
247275
while (!SYSCTRL->PCLKSR.bit.DFLLRDY) {
248276
}
249277

250-
// Enable 32768 Hz on GCLK1 for consistency
251-
GCLK->GENDIV.reg = GCLK_GENDIV_ID(1) | GCLK_GENDIV_DIV(48016384 / 32768);
252-
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_ID(1);
278+
// Connect the GCLK1 to the XOSC32KULP
279+
GCLK->GENDIV.reg = GCLK_GENDIV_ID(1) | GCLK_GENDIV_DIV(1);
280+
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSCULP32K | GCLK_GENCTRL_ID(1);
253281
while (GCLK->STATUS.bit.SYNCBUSY) {
254282
}
255283
// Set GCLK8 to 1 kHz.

Diff for: ports/samd/mcu/samd21/mpconfigmcu.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@
4040

4141
#define CPU_FREQ (48000000)
4242
#define DFLL48M_FREQ (48000000)
43-
#define MAX_CPU_FREQ (48000000)
43+
#define MAX_CPU_FREQ (54000000)
44+
#define FDPLL_REF_FREQ (32768)
4445

4546
#define IRQ_PRI_PENDSV ((1 << __NVIC_PRIO_BITS) - 1)
4647

0 commit comments

Comments
 (0)