aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2019-02-21 12:32:51 -0500
committerKevin O'Connor <kevin@koconnor.net>2019-02-22 09:07:26 -0500
commitda68da7a63322c300acf574f0068bb09b8073916 (patch)
tree3d84f4de9cff959ea7b7fbc550fa8086fa2c3abc /src
parentd452a1de482e7ab56c230bff39498c5d84404126 (diff)
downloadkutter-da68da7a63322c300acf574f0068bb09b8073916.tar.gz
kutter-da68da7a63322c300acf574f0068bb09b8073916.tar.xz
kutter-da68da7a63322c300acf574f0068bb09b8073916.zip
atsamd: Add support for reference clock selection to SAMD21
Add support for using the internal clocks (with USB clock recovery mode if available) on the SAMD21. Don't use the internal clock if the external crystal is requested (instead use the PLL synced to the external 32Khz signal). Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
Diffstat (limited to 'src')
-rw-r--r--src/atsamd/Kconfig1
-rw-r--r--src/atsamd/clock.c84
2 files changed, 58 insertions, 27 deletions
diff --git a/src/atsamd/Kconfig b/src/atsamd/Kconfig
index 8cc9bea0..b9bcbab2 100644
--- a/src/atsamd/Kconfig
+++ b/src/atsamd/Kconfig
@@ -54,7 +54,6 @@ config CLOCK_FREQ
default 120000000 if MACH_SAMD51
choice
- depends on MACH_SAMD51
prompt "Clock Reference"
config CLOCK_REF_INTERNAL
bool "Internal clock"
diff --git a/src/atsamd/clock.c b/src/atsamd/clock.c
index 46319f30..1a97fe52 100644
--- a/src/atsamd/clock.c
+++ b/src/atsamd/clock.c
@@ -9,7 +9,6 @@
// The "generic clock generators" that are configured
#define CLKGEN_MAIN 0
-#define CLKGEN_32K 1
#define CLKGEN_ULP32K 2
#define FREQ_MAIN 48000000
@@ -49,41 +48,74 @@ get_pclock_frequency(uint32_t pclk_id)
return FREQ_MAIN;
}
-void
-SystemInit(void)
+// Initialize the clocks using an external 32K crystal
+static void
+clock_init_32k(void)
{
- // Setup flash to work with 48Mhz clock
- NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_RWS_HALF;
-
- // Reset GCLK
- GCLK->CTRL.reg = GCLK_CTRL_SWRST;
- while (GCLK->CTRL.reg & GCLK_CTRL_SWRST)
- ;
-
- // Enable external 32Khz crystal and route to CLKGEN_32K
+ // Enable external 32Khz crystal
uint32_t val = (SYSCTRL_XOSC32K_STARTUP(6)
| SYSCTRL_XOSC32K_XTALEN | SYSCTRL_XOSC32K_EN32K);
SYSCTRL->XOSC32K.reg = val;
SYSCTRL->XOSC32K.reg = val | SYSCTRL_XOSC32K_ENABLE;
while (!(SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_XOSC32KRDY))
;
- gen_clock(CLKGEN_32K, GCLK_GENCTRL_SRC_XOSC32K);
- // Configure DFLL48M clock (with CLKGEN_32K as reference)
- route_pclock(SYSCTRL_GCLK_ID_DFLL48, CLKGEN_32K);
- SYSCTRL->DFLLCTRL.reg = 0;
+ // Generate 48Mhz clock on DPLL (with XOSC32K as reference)
+ SYSCTRL->DPLLCTRLA.reg = 0;
uint32_t mul = DIV_ROUND_CLOSEST(FREQ_MAIN, FREQ_32K);
- SYSCTRL->DFLLMUL.reg = (SYSCTRL_DFLLMUL_CSTEP(31)
- | SYSCTRL_DFLLMUL_FSTEP(511)
- | SYSCTRL_DFLLMUL_MUL(mul));
- SYSCTRL->DFLLCTRL.reg = (SYSCTRL_DFLLCTRL_MODE | SYSCTRL_DFLLCTRL_WAITLOCK
- | SYSCTRL_DFLLCTRL_QLDIS
- | SYSCTRL_DFLLCTRL_ENABLE);
- uint32_t ready = (SYSCTRL_PCLKSR_DFLLRDY | SYSCTRL_PCLKSR_DFLLLCKC
- | SYSCTRL_PCLKSR_DFLLLCKF);
- while ((SYSCTRL->PCLKSR.reg & ready) != ready)
+ SYSCTRL->DPLLRATIO.reg = SYSCTRL_DPLLRATIO_LDR(mul - 1);
+ SYSCTRL->DPLLCTRLB.reg = SYSCTRL_DPLLCTRLB_LBYPASS;
+ SYSCTRL->DPLLCTRLA.reg = SYSCTRL_DPLLCTRLA_ENABLE;
+ uint32_t mask = SYSCTRL_DPLLSTATUS_CLKRDY | SYSCTRL_DPLLSTATUS_LOCK;
+ while ((SYSCTRL->DPLLSTATUS.reg & mask) != mask)
+ ;
+
+ // Switch main clock to DPLL clock
+ gen_clock(CLKGEN_MAIN, GCLK_GENCTRL_SRC_DPLL96M);
+}
+
+// Initialize clocks from factory calibrated internal clock
+static void
+clock_init_internal(void)
+{
+ // Configure DFLL48M clock in open loop mode
+ SYSCTRL->DFLLCTRL.reg = 0;
+ uint32_t coarse = GET_FUSE(FUSES_DFLL48M_COARSE_CAL);
+ uint32_t fine = GET_FUSE(FUSES_DFLL48M_FINE_CAL);
+ SYSCTRL->DFLLVAL.reg = (SYSCTRL_DFLLVAL_COARSE(coarse)
+ | SYSCTRL_DFLLVAL_FINE(fine));
+ if (CONFIG_USBSERIAL) {
+ // Enable USB clock recovery mode
+ uint32_t mul = DIV_ROUND_CLOSEST(FREQ_MAIN, 1000);
+ SYSCTRL->DFLLMUL.reg = (SYSCTRL_DFLLMUL_FSTEP(10)
+ | SYSCTRL_DFLLMUL_MUL(mul));
+ SYSCTRL->DFLLCTRL.reg = (SYSCTRL_DFLLCTRL_MODE | SYSCTRL_DFLLCTRL_USBCRM
+ | SYSCTRL_DFLLCTRL_CCDIS
+ | SYSCTRL_DFLLCTRL_ENABLE);
+ } else {
+ SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_ENABLE;
+ }
+ while (!(SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLRDY))
;
// Switch main clock to DFLL48M clock
- gen_clock(CLKGEN_MAIN, GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_IDC);
+ gen_clock(CLKGEN_MAIN, GCLK_GENCTRL_SRC_DFLL48M);
+}
+
+void
+SystemInit(void)
+{
+ // Setup flash to work with 48Mhz clock
+ NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_RWS_HALF;
+
+ // Reset GCLK
+ GCLK->CTRL.reg = GCLK_CTRL_SWRST;
+ while (GCLK->CTRL.reg & GCLK_CTRL_SWRST)
+ ;
+
+ // Init clocks
+ if (CONFIG_CLOCK_REF_X32K)
+ clock_init_32k();
+ else
+ clock_init_internal();
}