Skip to content

Commit ab7ddfd

Browse files
committed
atmel-samd/samd51: Refactor clock setup
Refactor the convoluted asf4 clock setup into something more readable. enable_clock_generator() has 2 changes: - Set 'Output enabled' to match the current clock setup - Handle divisors above 511 Add an enable_clock_generator_sync() version which makes it possible to setup clocks without waiting for syncing. The bootup would hang without this. I have checked these registers: NVMCTRL->CTRLA = 0x00000004 Peripheral clocks (only non-zero shown): PCHCTRL[1]=0x00000045 PCHCTRL[10]=0x00000041 Generator clocks (only non-zero shown): GENCTRL[0] = 0x00010907 GENCTRL[1] = 0x00010906 -GENCTRL[2] = 0x00041104 +GENCTRL[2] = 0x00200904 GENCTRL[4] = 0x00010907 GENCTRL[5] = 0x00180906 DFLL clock: OSCCTRL->DFLLCTRLA = 0x00000082 OSCCTRL->DFLLCTRLB = 0x00000000 OSCCTRL->DFLLVAL = 0x00008082 OSCCTRL->DFLLMUL = 0x00000000 DPLL clocks: OSCCTRL->Dpll[0].DPLLCTRLA=0x00000002 OSCCTRL->Dpll[0].DPLLCTRLB=0x00000000 OSCCTRL->Dpll[0].DPLLRATIO=0x0000003b OSCCTRL->Dpll[1].DPLLCTRLA=0x00000080 OSCCTRL->Dpll[1].DPLLCTRLB=0x00000020 OSCCTRL->Dpll[1].DPLLRATIO=0x00000000 OSC32KCTRL clock: OSC32KCTRL->RTCCTRL = 0x00000000 OSC32KCTRL->XOSC32K = 0x00002082 OSC32KCTRL->CFDCTRL = 0x00000000 OSC32KCTRL->EVCTRL = 0x00000000 OSC32KCTRL->OSCULP32K = 0x00002300 Only gen2 changed which is due to samd51 having more bits in the simple division register so DIVSEL wasn't necessary, and it didn't have OE set.
1 parent 5c6aea9 commit ab7ddfd

File tree

2 files changed

+62
-6
lines changed

2 files changed

+62
-6
lines changed

ports/atmel-samd/samd51_clocks.c

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,74 @@ void disconnect_gclk_from_peripheral(uint8_t gclk, uint8_t peripheral) {
5252
GCLK->PCHCTRL[peripheral].reg = 0;
5353
}
5454

55+
static void enable_clock_generator_sync(uint8_t gclk, uint32_t source, uint16_t divisor, bool sync) {
56+
uint32_t divsel = 0;
57+
// The datasheet says 8 bits and max value of 512, how is that possible?
58+
if (divisor > 255) { // Generator 1 has 16 bits
59+
divsel = GCLK_GENCTRL_DIVSEL;
60+
for (int i = 15; i > 0; i--) {
61+
if (divisor & (1 << i)) {
62+
divisor = i - 1;
63+
break;
64+
}
65+
}
66+
}
67+
68+
GCLK->GENCTRL[gclk].reg = GCLK_GENCTRL_SRC(source) | GCLK_GENCTRL_DIV(divisor) | divsel | GCLK_GENCTRL_OE | GCLK_GENCTRL_GENEN;
69+
if (sync)
70+
while ((GCLK->SYNCBUSY.vec.GENCTRL & (1 << gclk)) != 0) {}
71+
}
72+
5573
void enable_clock_generator(uint8_t gclk, uint32_t source, uint16_t divisor) {
56-
GCLK->GENCTRL[gclk].reg = GCLK_GENCTRL_SRC(source) | GCLK_GENCTRL_DIV(divisor) | GCLK_GENCTRL_GENEN;
57-
while ((GCLK->SYNCBUSY.vec.GENCTRL & (1 << gclk)) != 0) {}
74+
enable_clock_generator_sync(gclk, source, divisor, true);
5875
}
5976

6077
void disable_clock_generator(uint8_t gclk) {
6178
GCLK->GENCTRL[gclk].reg = 0;
6279
while ((GCLK->SYNCBUSY.vec.GENCTRL & (1 << gclk)) != 0) {}
6380
}
6481

82+
static void init_clock_source_osculp32k(void) {
83+
// Calibration value is loaded at startup
84+
OSC32KCTRL->OSCULP32K.bit.EN1K = 0;
85+
OSC32KCTRL->OSCULP32K.bit.EN32K = 0;
86+
}
87+
88+
static void init_clock_source_xosc32k(void) {
89+
OSC32KCTRL->XOSC32K.reg = OSC32KCTRL_XOSC32K_ONDEMAND |
90+
OSC32KCTRL_XOSC32K_ENABLE |
91+
OSC32KCTRL_XOSC32K_CGM(1);
92+
}
93+
94+
static void init_clock_source_dpll0(void)
95+
{
96+
GCLK->PCHCTRL[OSCCTRL_GCLK_ID_FDPLL0].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(5);
97+
OSCCTRL->Dpll[0].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDRFRAC(0) | OSCCTRL_DPLLRATIO_LDR(59);
98+
OSCCTRL->Dpll[0].DPLLCTRLB.reg = OSCCTRL_DPLLCTRLB_REFCLK(0);
99+
OSCCTRL->Dpll[0].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_ENABLE;
100+
101+
while (!(OSCCTRL->Dpll[0].DPLLSTATUS.bit.LOCK || OSCCTRL->Dpll[0].DPLLSTATUS.bit.CLKRDY)) {}
102+
}
103+
104+
void clock_init(void) {
105+
// DFLL48M is enabled by default
106+
107+
init_clock_source_osculp32k();
108+
init_clock_source_xosc32k();
109+
110+
OSC32KCTRL->RTCCTRL.bit.RTCSEL = OSC32KCTRL_RTCCTRL_RTCSEL_ULP1K_Val;
111+
112+
MCLK->CPUDIV.reg = MCLK_CPUDIV_DIV(1);
113+
114+
enable_clock_generator_sync(0, GCLK_GENCTRL_SRC_DPLL0_Val, 1, false);
115+
enable_clock_generator_sync(1, GCLK_GENCTRL_SRC_DFLL_Val, 1, false);
116+
enable_clock_generator_sync(2, GCLK_GENCTRL_SRC_OSCULP32K_Val, 32, false);
117+
enable_clock_generator_sync(4, GCLK_GENCTRL_SRC_DPLL0_Val, 1, false);
118+
enable_clock_generator_sync(5, GCLK_GENCTRL_SRC_DFLL_Val, 24, false);
119+
120+
init_clock_source_dpll0();
121+
}
122+
65123
static bool clk_enabled(uint8_t clk) {
66124
return GCLK->PCHCTRL[clk].bit.CHEN;
67125
}

ports/atmel-samd/supervisor/port.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,11 +181,9 @@ safe_mode_t port_init(void) {
181181
#ifdef SAMD21
182182
hri_nvmctrl_set_CTRLB_RWS_bf(NVMCTRL, 2);
183183
_pm_init();
184-
clock_init();
185-
#endif
186-
#ifdef SAMD51
187-
init_mcu();
188184
#endif
185+
clock_init();
186+
189187
board_init();
190188

191189
// Configure millisecond timer initialization.

0 commit comments

Comments
 (0)