Skip to content

Commit 991bf4a

Browse files
committed
cpu/samd5: clock initialisation XOSC32 and FDPLL
- valid clock XOSC32 startup time - avoid gclk roundtrip for FDPLL when XOSC32 is used
1 parent cfaa76b commit 991bf4a

File tree

1 file changed

+48
-15
lines changed

1 file changed

+48
-15
lines changed

cpu/samd5x/cpu.c

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
* @}
1818
*/
1919

20+
#include "busy_wait.h"
2021
#include <assert.h>
2122

2223
#include "cpu.h"
@@ -59,7 +60,8 @@
5960
# endif
6061

6162
# if (CLOCK_CORECLOCK > SAM0_XOSC_FREQ_HZ)
62-
# error When using an external oscillator for the main clock, the CPU frequency can't exceed it's frequency.
63+
# error When using an external oscillator for the main clock,\
64+
the CPU frequency can't exceed it's frequency.
6365
# endif
6466

6567
# define USE_DPLL 0
@@ -102,12 +104,16 @@ static void xosc32k_init(void)
102104
return;
103105
}
104106

107+
/* Startup should be a valid value 0 - 6 (~63 ms to ~8000 ms) see manual 7 is reserved
108+
* table 29-2 : time_for_startup_val[] = {63ms, 125ms, 500ms, 1sec, 2sec, 4sec, 8sec}
109+
* this delay will happen only when the system is powered on or the XOSC32
110+
* is re-enabled after being disabled for e.g.: standby*/
105111
OSC32KCTRL->XOSC32K.reg = OSC32KCTRL_XOSC32K_ENABLE
106112
| OSC32KCTRL_XOSC32K_EN1K
107113
| OSC32KCTRL_XOSC32K_EN32K
108114
| OSC32KCTRL_XOSC32K_RUNSTDBY
109115
| OSC32KCTRL_XOSC32K_XTALEN
110-
| OSC32KCTRL_XOSC32K_STARTUP(7);
116+
| OSC32KCTRL_XOSC32K_STARTUP(3); /* 3 ^= ~1sec see above or manual*/
111117

112118
while (!(OSC32KCTRL->STATUS.reg & OSC32KCTRL_STATUS_XOSC32KRDY)) {}
113119
}
@@ -197,25 +203,44 @@ static void fdpll_init_nolock(uint8_t idx, uint32_t f_cpu, uint8_t flags)
197203
return;
198204
}
199205

200-
/* Source the DPLL from 32kHz GCLK1 ( equivalent to ((f_cpu << 5) / 32768) ) */
201-
const uint32_t LDR = (f_cpu >> 10);
202-
203206
/* disable the DPLL before changing the configuration */
204-
OSCCTRL->Dpll[idx].DPLLCTRLA.reg &= ~OSCCTRL_DPLLCTRLA_ENABLE;
207+
OSCCTRL->Dpll[idx].DPLLCTRLA.reg = 0;
205208
while (OSCCTRL->Dpll[idx].DPLLSYNCBUSY.reg) {}
206209

207-
/* set DPLL clock source */
208-
GCLK->PCHCTRL[OSCCTRL_GCLK_ID_FDPLL0 + idx].reg = GCLK_PCHCTRL_GEN(1) | GCLK_PCHCTRL_CHEN;
209-
while (!(GCLK->PCHCTRL[OSCCTRL_GCLK_ID_FDPLL0 + idx].reg & GCLK_PCHCTRL_CHEN)) {}
210+
uint32_t ctrlb = 0;
210211

211-
OSCCTRL->Dpll[idx].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDRFRAC(LDR & 0x1F)
212-
| OSCCTRL_DPLLRATIO_LDR((LDR >> 5) - 1);
212+
/* HW revision before F (A and D) might false unlock -> LBYPASS and WUF */
213+
unsigned rev = (DSU->DID.reg & DSU_DID_REVISION_Msk) >> DSU_DID_REVISION_Pos;
214+
if ('A' + rev < 'F') {
215+
ctrlb |= OSCCTRL_DPLLCTRLB_WUF | OSCCTRL_DPLLCTRLB_LBYPASS;
216+
}
213217

214-
/* Without LBYPASS, startup takes very long, see errata section 2.13. */
215-
OSCCTRL->Dpll[idx].DPLLCTRLB.reg = OSCCTRL_DPLLCTRLB_REFCLK_GCLK
216-
| OSCCTRL_DPLLCTRLB_WUF
217-
| OSCCTRL_DPLLCTRLB_LBYPASS;
218+
/* Without LBYPASS, startup takes very long, see errata section 2.13.
219+
* according to the documentation several milliseconds
220+
* (critical for some application not so much for other)*/
221+
if (EXTERNAL_OSC32_SOURCE) {
222+
/* Source the DPLL from 32kHz XOSC32 ( equivalent to ((f_cpu << 5) / 32768) ) */
223+
const uint32_t LDR = (f_cpu >> 10);
224+
OSCCTRL->Dpll[idx].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDRFRAC(LDR & 0x1F)
225+
| OSCCTRL_DPLLRATIO_LDR((LDR >> 5) - 1);
218226

227+
ctrlb |= OSCCTRL_DPLLCTRLB_REFCLK_XOSC32;
228+
OSCCTRL->Dpll[idx].DPLLCTRLB.reg = ctrlb;
229+
}
230+
else {
231+
/* TODO find a better fallback source (eg 48MCLK routed though gclk divided down to 1MHz)
232+
* until then the frequency might not be defined if source is low power internal 32kHz*/
233+
/* set DPLL clock source */
234+
GCLK->PCHCTRL[OSCCTRL_GCLK_ID_FDPLL0 + idx].reg = GCLK_PCHCTRL_GEN(1) | GCLK_PCHCTRL_CHEN;
235+
while (!(GCLK->PCHCTRL[OSCCTRL_GCLK_ID_FDPLL0 + idx].reg & GCLK_PCHCTRL_CHEN)) {}
236+
/* Source the DPLL from 32kHz GCLK1 ( equivalent to ((f_cpu << 5) / 32768) )
237+
* avoid the routing through gclk when XOSC32 is the source */
238+
const uint32_t LDR = (f_cpu >> 10);
239+
OSCCTRL->Dpll[idx].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDRFRAC(LDR & 0x1F)
240+
| OSCCTRL_DPLLRATIO_LDR((LDR >> 5) - 1);
241+
ctrlb |= OSCCTRL_DPLLCTRLB_REFCLK_GCLK;
242+
}
243+
OSCCTRL->Dpll[idx].DPLLCTRLB.reg = ctrlb;
219244
OSCCTRL->Dpll[idx].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_ENABLE | flags;
220245

221246
while (OSCCTRL->Dpll[idx].DPLLSYNCBUSY.reg) {}
@@ -225,6 +250,14 @@ static void fdpll_lock(uint8_t idx)
225250
{
226251
const uint32_t flags = (OSCCTRL_DPLLSTATUS_CLKRDY | OSCCTRL_DPLLSTATUS_LOCK);
227252
while (!((OSCCTRL->Dpll[idx].DPLLSTATUS.reg & flags) == flags)) {}
253+
254+
/* TODO make this configurable currently prefer correctness over time */
255+
/* HW revision before F (A and D) might false unlock -> LBYPASS and WUF
256+
* doing that we need to ensure the PLL had enough time to lock(at least 10 ms) */
257+
unsigned rev = (DSU->DID.reg & DSU_DID_REVISION_Msk) >> DSU_DID_REVISION_Pos;
258+
if ('A' + rev < 'F') {
259+
busy_wait_us(10 * US_PER_MS);
260+
}
228261
}
229262

230263
static void gclk_connect(uint8_t id, uint8_t src, uint32_t flags)

0 commit comments

Comments
 (0)