Skip to content

cpu/samd5: improve clock initialisation XOSC32 and FDPLL #21631

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 4, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 48 additions & 15 deletions cpu/samd5x/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* @}
*/

#include "busy_wait.h"
#include <assert.h>

#include "cpu.h"
Expand Down Expand Up @@ -59,7 +60,8 @@
# endif

# if (CLOCK_CORECLOCK > SAM0_XOSC_FREQ_HZ)
# error When using an external oscillator for the main clock, the CPU frequency can't exceed it's frequency.
# error When using an external oscillator for the main clock,\

Check warning on line 63 in cpu/samd5x/cpu.c

View workflow job for this annotation

GitHub Actions / static-tests

keyword 'for' not followed by a single space
the CPU frequency can't exceed it's frequency.
# endif

# define USE_DPLL 0
Expand All @@ -73,7 +75,7 @@
#else /* !USE_XOSC_ONLY */

/* Main clock > 48 MHz -> use DPLL, otherwise use DFLL */
# define USE_DPLL (CLOCK_CORECLOCK > SAM0_DFLL_FREQ_HZ)

Check warning on line 78 in cpu/samd5x/cpu.c

View workflow job for this annotation

GitHub Actions / static-tests

full block {} expected in the control structure
# define USE_DFLL 1
# define USE_XOSC 0

Expand Down Expand Up @@ -102,12 +104,16 @@
return;
}

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

while (!(OSC32KCTRL->STATUS.reg & OSC32KCTRL_STATUS_XOSC32KRDY)) {}
}
Expand Down Expand Up @@ -174,7 +180,7 @@
#ifdef OSCCTRL_DFLLCTRLB_WAITLOCK
| OSCCTRL_DFLLCTRLB_WAITLOCK
#endif
;

Check warning on line 183 in cpu/samd5x/cpu.c

View workflow job for this annotation

GitHub Actions / static-tests

semicolon is isolated from other tokens

/* workaround for Errata 2.8.3 DFLLVAL.FINE Value When DFLL48M Re-enabled */
OSCCTRL->DFLLMUL.reg = 0; /* Write new DFLLMULL configuration */
Expand All @@ -197,25 +203,44 @@
return;
}

/* Source the DPLL from 32kHz GCLK1 ( equivalent to ((f_cpu << 5) / 32768) ) */
const uint32_t LDR = (f_cpu >> 10);

/* disable the DPLL before changing the configuration */
OSCCTRL->Dpll[idx].DPLLCTRLA.reg &= ~OSCCTRL_DPLLCTRLA_ENABLE;
OSCCTRL->Dpll[idx].DPLLCTRLA.reg = 0;
while (OSCCTRL->Dpll[idx].DPLLSYNCBUSY.reg) {}

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

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

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

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

while (OSCCTRL->Dpll[idx].DPLLSYNCBUSY.reg) {}
Expand All @@ -225,6 +250,14 @@
{
const uint32_t flags = (OSCCTRL_DPLLSTATUS_CLKRDY | OSCCTRL_DPLLSTATUS_LOCK);
while (!((OSCCTRL->Dpll[idx].DPLLSTATUS.reg & flags) == flags)) {}

/* TODO make this configurable currently prefer correctness over time */
/* HW revision before F (A and D) might false unlock -> LBYPASS and WUF
* doing that we need to ensure the PLL had enough time to lock(at least 10 ms) */
unsigned rev = (DSU->DID.reg & DSU_DID_REVISION_Msk) >> DSU_DID_REVISION_Pos;
if ('A' + rev < 'F') {
busy_wait_us(10 * US_PER_MS);
}
}

static void gclk_connect(uint8_t id, uint8_t src, uint32_t flags)
Expand Down Expand Up @@ -345,7 +378,7 @@
#ifdef MODULE_PERIPH_GPIO_IRQ
| MCLK_APBAMASK_EIC
#endif
;

Check warning on line 381 in cpu/samd5x/cpu.c

View workflow job for this annotation

GitHub Actions / static-tests

semicolon is isolated from other tokens

MCLK->APBBMASK.reg = 0
#ifdef MODULE_PERIPH_FLASHPAGE
Expand All @@ -354,7 +387,7 @@
#ifdef MODULE_PERIPH_GPIO
| MCLK_APBBMASK_PORT
#endif
;

Check warning on line 390 in cpu/samd5x/cpu.c

View workflow job for this annotation

GitHub Actions / static-tests

semicolon is isolated from other tokens

MCLK->APBCMASK.reg = 0;
MCLK->APBDMASK.reg = 0;
Expand Down