17
17
* @}
18
18
*/
19
19
20
+ #include "busy_wait.h"
20
21
#include <assert.h>
21
22
22
23
#include "cpu.h"
59
60
# endif
60
61
61
62
# 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.
63
65
# endif
64
66
65
67
# define USE_DPLL 0
@@ -102,12 +104,16 @@ static void xosc32k_init(void)
102
104
return ;
103
105
}
104
106
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*/
105
111
OSC32KCTRL -> XOSC32K .reg = OSC32KCTRL_XOSC32K_ENABLE
106
112
| OSC32KCTRL_XOSC32K_EN1K
107
113
| OSC32KCTRL_XOSC32K_EN32K
108
114
| OSC32KCTRL_XOSC32K_RUNSTDBY
109
115
| OSC32KCTRL_XOSC32K_XTALEN
110
- | OSC32KCTRL_XOSC32K_STARTUP (7 );
116
+ | OSC32KCTRL_XOSC32K_STARTUP (3 ); /* 3 ^= ~1sec see above or manual*/
111
117
112
118
while (!(OSC32KCTRL -> STATUS .reg & OSC32KCTRL_STATUS_XOSC32KRDY )) {}
113
119
}
@@ -197,25 +203,44 @@ static void fdpll_init_nolock(uint8_t idx, uint32_t f_cpu, uint8_t flags)
197
203
return ;
198
204
}
199
205
200
- /* Source the DPLL from 32kHz GCLK1 ( equivalent to ((f_cpu << 5) / 32768) ) */
201
- const uint32_t LDR = (f_cpu >> 10 );
202
-
203
206
/* disable the DPLL before changing the configuration */
204
- OSCCTRL -> Dpll [idx ].DPLLCTRLA .reg &= ~ OSCCTRL_DPLLCTRLA_ENABLE ;
207
+ OSCCTRL -> Dpll [idx ].DPLLCTRLA .reg = 0 ;
205
208
while (OSCCTRL -> Dpll [idx ].DPLLSYNCBUSY .reg ) {}
206
209
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 ;
210
211
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
+ }
213
217
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 );
218
226
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 ;
219
244
OSCCTRL -> Dpll [idx ].DPLLCTRLA .reg = OSCCTRL_DPLLCTRLA_ENABLE | flags ;
220
245
221
246
while (OSCCTRL -> Dpll [idx ].DPLLSYNCBUSY .reg ) {}
@@ -225,6 +250,14 @@ static void fdpll_lock(uint8_t idx)
225
250
{
226
251
const uint32_t flags = (OSCCTRL_DPLLSTATUS_CLKRDY | OSCCTRL_DPLLSTATUS_LOCK );
227
252
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
+ }
228
261
}
229
262
230
263
static void gclk_connect (uint8_t id , uint8_t src , uint32_t flags )
0 commit comments