aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/atsamd/Kconfig4
-rw-r--r--src/atsamd/samd51_clock.c101
2 files changed, 64 insertions, 41 deletions
diff --git a/src/atsamd/Kconfig b/src/atsamd/Kconfig
index 7d6e1357..8cc9bea0 100644
--- a/src/atsamd/Kconfig
+++ b/src/atsamd/Kconfig
@@ -56,10 +56,10 @@ config CLOCK_FREQ
choice
depends on MACH_SAMD51
prompt "Clock Reference"
+ config CLOCK_REF_INTERNAL
+ bool "Internal clock"
config CLOCK_REF_X32K
bool "32.768Khz crystal"
- config CLOCK_REF_INTERNAL
- bool "Factory calibration"
endchoice
choice
diff --git a/src/atsamd/samd51_clock.c b/src/atsamd/samd51_clock.c
index 363aa19e..040f29b7 100644
--- a/src/atsamd/samd51_clock.c
+++ b/src/atsamd/samd51_clock.c
@@ -9,7 +9,6 @@
// The "generic clock generators" that are configured
#define CLKGEN_MAIN 0
-#define CLKGEN_32K 2
#define CLKGEN_48M 3
#define CLKGEN_2M 4
@@ -53,57 +52,89 @@ get_pclock_frequency(uint32_t pclk_id)
return FREQ_48M;
}
+// Configure a dpll to a given clock multiplier
+static void
+config_dpll(uint32_t pll, uint32_t mul, uint32_t ctrlb)
+{
+ OSCCTRL->Dpll[pll].DPLLCTRLA.reg = 0;
+ while (OSCCTRL->Dpll[0].DPLLSYNCBUSY.reg & OSCCTRL_DPLLSYNCBUSY_ENABLE)
+ ;
+ OSCCTRL->Dpll[pll].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDR(mul - 1);
+ while (OSCCTRL->Dpll[0].DPLLSYNCBUSY.reg & OSCCTRL_DPLLSYNCBUSY_DPLLRATIO)
+ ;
+ OSCCTRL->Dpll[pll].DPLLCTRLB.reg = ctrlb | OSCCTRL_DPLLCTRLB_LBYPASS;
+ OSCCTRL->Dpll[pll].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_ENABLE;
+ uint32_t mask = OSCCTRL_DPLLSTATUS_CLKRDY | OSCCTRL_DPLLSTATUS_LOCK;
+ while ((OSCCTRL->Dpll[pll].DPLLSTATUS.reg & mask) != mask)
+ ;
+}
+
+// Configure the dfll
+static void
+config_dfll(uint32_t dfllmul, uint32_t ctrlb)
+{
+ // Disable the dfllmul and reenable in this order due to chip errata
+ OSCCTRL->DFLLCTRLA.reg = 0;
+ while (OSCCTRL->DFLLSYNC.reg & OSCCTRL_DFLLSYNC_ENABLE)
+ ;
+ OSCCTRL->DFLLMUL.reg = dfllmul;
+ while (OSCCTRL->DFLLSYNC.reg & OSCCTRL_DFLLSYNC_DFLLMUL)
+ ;
+ OSCCTRL->DFLLCTRLB.reg = 0;
+ while (OSCCTRL->DFLLSYNC.reg & OSCCTRL_DFLLSYNC_DFLLCTRLB)
+ ;
+ OSCCTRL->DFLLCTRLA.reg = OSCCTRL_DFLLCTRLA_ENABLE;
+ while (OSCCTRL->DFLLSYNC.reg & OSCCTRL_DFLLSYNC_ENABLE)
+ ;
+ OSCCTRL->DFLLVAL.reg = OSCCTRL->DFLLVAL.reg;
+ while(OSCCTRL->DFLLSYNC.reg & OSCCTRL_DFLLSYNC_DFLLVAL)
+ ;
+ OSCCTRL->DFLLCTRLB.reg = ctrlb;
+ while (OSCCTRL->DFLLSYNC.reg & OSCCTRL_DFLLSYNC_DFLLCTRLB)
+ ;
+}
+
// Initialize the clocks using an external 32K crystal
static void
clock_init_32k(void)
{
- // Enable external 32Khz crystal and route to CLKGEN_32K
+ // Enable external 32Khz crystal
uint32_t val = (OSC32KCTRL_XOSC32K_ENABLE | OSC32KCTRL_XOSC32K_EN32K
| OSC32KCTRL_XOSC32K_CGM_XT | OSC32KCTRL_XOSC32K_XTALEN);
OSC32KCTRL->XOSC32K.reg = val;
while (!(OSC32KCTRL->STATUS.reg & OSC32KCTRL_STATUS_XOSC32KRDY))
;
- gen_clock(CLKGEN_32K, GCLK_GENCTRL_SRC_XOSC32K);
- // Generate 120Mhz clock on PLL0 (with CLKGEN_32K as reference)
- route_pclock(OSCCTRL_GCLK_ID_FDPLL0, CLKGEN_32K);
+ // Generate 120Mhz clock on PLL0 (with XOSC32 as reference)
uint32_t mul = DIV_ROUND_CLOSEST(FREQ_MAIN, FREQ_32K);
- OSCCTRL->Dpll[0].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDR(mul - 1);
- while (OSCCTRL->Dpll[0].DPLLSYNCBUSY.reg & OSCCTRL_DPLLSYNCBUSY_DPLLRATIO)
- ;
- OSCCTRL->Dpll[0].DPLLCTRLB.reg = (OSCCTRL_DPLLCTRLB_REFCLK_GCLK
- | OSCCTRL_DPLLCTRLB_LBYPASS);
- OSCCTRL->Dpll[0].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_ENABLE;
- uint32_t mask = OSCCTRL_DPLLSTATUS_CLKRDY | OSCCTRL_DPLLSTATUS_LOCK;
- while ((OSCCTRL->Dpll[0].DPLLSTATUS.reg & mask) != mask)
- ;
+ config_dpll(0, mul, OSCCTRL_DPLLCTRLB_REFCLK_XOSC32);
// Switch main clock to 120Mhz PLL0
gen_clock(CLKGEN_MAIN, GCLK_GENCTRL_SRC_DPLL0);
- // Configure DFLL48M clock (with CLKGEN_32K as reference)
- OSCCTRL->DFLLCTRLA.reg = 0;
- route_pclock(OSCCTRL_GCLK_ID_DFLL48, CLKGEN_32K);
+ // Generate 48Mhz clock on PLL1 (with XOSC32 as reference)
mul = DIV_ROUND_CLOSEST(FREQ_48M, FREQ_32K);
- OSCCTRL->DFLLMUL.reg = (OSCCTRL_DFLLMUL_CSTEP(31)
- | OSCCTRL_DFLLMUL_FSTEP(511)
- | OSCCTRL_DFLLMUL_MUL(mul));
- while (OSCCTRL->DFLLSYNC.reg & OSCCTRL_DFLLSYNC_DFLLMUL)
- ;
- OSCCTRL->DFLLCTRLB.reg = (OSCCTRL_DFLLCTRLB_MODE | OSCCTRL_DFLLCTRLB_QLDIS
- | OSCCTRL_DFLLCTRLB_WAITLOCK);
- while (OSCCTRL->DFLLSYNC.reg & OSCCTRL_DFLLSYNC_DFLLCTRLB)
- ;
- OSCCTRL->DFLLCTRLA.reg = OSCCTRL_DFLLCTRLA_ENABLE;
- while (!(OSCCTRL->STATUS.reg & OSCCTRL_STATUS_DFLLRDY))
- ;
- gen_clock(CLKGEN_48M, GCLK_GENCTRL_SRC_DFLL);
+ config_dpll(1, mul, OSCCTRL_DPLLCTRLB_REFCLK_XOSC32);
+ gen_clock(CLKGEN_48M, GCLK_GENCTRL_SRC_DPLL1);
}
// Initialize clocks from factory calibrated internal clock
static void
clock_init_internal(void)
{
+ // Enable USB clock recovery mode if applicable
+ if (CONFIG_USBSERIAL) {
+ // Temporarily switch main clock to internal 32K clock
+ gen_clock(CLKGEN_MAIN, GCLK_GENCTRL_SRC_OSCULP32K);
+
+ // Configure DFLL48M clock (with USB 1Khz SOF as reference)
+ uint32_t mul = DIV_ROUND_CLOSEST(FREQ_48M, 1000);
+ uint32_t dfllmul = OSCCTRL_DFLLMUL_FSTEP(10) | OSCCTRL_DFLLMUL_MUL(mul);
+ uint32_t ctrlb = (OSCCTRL_DFLLCTRLB_MODE | OSCCTRL_DFLLCTRLB_USBCRM
+ | OSCCTRL_DFLLCTRLB_CCDIS);
+ config_dfll(dfllmul, ctrlb);
+ }
+
// Route factory calibrated DFLL48M to CLKGEN_48M
gen_clock(CLKGEN_48M, GCLK_GENCTRL_SRC_DFLL);
@@ -114,15 +145,7 @@ clock_init_internal(void)
// Generate 120Mhz clock on PLL0 (with CLKGEN_2M as reference)
route_pclock(OSCCTRL_GCLK_ID_FDPLL0, CLKGEN_2M);
uint32_t mul = DIV_ROUND_CLOSEST(FREQ_MAIN, FREQ_2M);
- OSCCTRL->Dpll[0].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDR(mul - 1);
- while (OSCCTRL->Dpll[0].DPLLSYNCBUSY.reg & OSCCTRL_DPLLSYNCBUSY_DPLLRATIO)
- ;
- OSCCTRL->Dpll[0].DPLLCTRLB.reg = (OSCCTRL_DPLLCTRLB_REFCLK_GCLK
- | OSCCTRL_DPLLCTRLB_LBYPASS);
- OSCCTRL->Dpll[0].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_ENABLE;
- uint32_t mask = OSCCTRL_DPLLSTATUS_CLKRDY | OSCCTRL_DPLLSTATUS_LOCK;
- while ((OSCCTRL->Dpll[0].DPLLSTATUS.reg & mask) != mask)
- ;
+ config_dpll(0, mul, OSCCTRL_DPLLCTRLB_REFCLK_GCLK);
// Switch main clock to 120Mhz PLL0
gen_clock(CLKGEN_MAIN, GCLK_GENCTRL_SRC_DPLL0);