2424 * THE SOFTWARE.
2525 */
2626
27+ #include <string.h>
28+ #include <stdlib.h>
29+
2730#include "peripherals/clocks.h"
2831
29- #include "hpl_gclk_config .h"
32+ #include "hal/include/hal_flash .h"
3033
3134#include "bindings/samd/Clock.h"
3235#include "shared-bindings/microcontroller/__init__.h"
3336
3437#include "py/runtime.h"
3538
39+ #ifdef EXPRESS_BOARD
40+ #define INTERNAL_CIRCUITPY_CONFIG_START_ADDR (0x00040000 - NVMCTRL_ROW_SIZE - CIRCUITPY_INTERNAL_NVM_SIZE)
41+ #else
42+ #define INTERNAL_CIRCUITPY_CONFIG_START_ADDR (0x00040000 - 0x010000 - NVMCTRL_ROW_SIZE - CIRCUITPY_INTERNAL_NVM_SIZE)
43+ #endif
44+
3645bool gclk_enabled (uint8_t gclk ) {
3746 common_hal_mcu_disable_interrupts ();
3847 // Explicitly do a byte write so the peripheral knows we're just wanting to read the channel
@@ -102,17 +111,48 @@ static void init_clock_source_xosc32k(void) {
102111 while (!SYSCTRL -> PCLKSR .bit .XOSC32KRDY ) {}
103112}
104113
105- static void init_clock_source_dfll48m (void ) {
114+ static void init_clock_source_dfll48m_xosc (void ) {
115+ SYSCTRL -> DFLLCTRL .reg = SYSCTRL_DFLLCTRL_ENABLE ;
116+ while (!SYSCTRL -> PCLKSR .bit .DFLLRDY ) {}
117+ SYSCTRL -> DFLLMUL .reg = SYSCTRL_DFLLMUL_CSTEP (0x1f / 4 ) |
118+ SYSCTRL_DFLLMUL_FSTEP (0xff / 4 ) |
119+ SYSCTRL_DFLLMUL_MUL (48000000 / 32768 );
120+ uint32_t coarse = (* ((uint32_t * )FUSES_DFLL48M_COARSE_CAL_ADDR ) & FUSES_DFLL48M_COARSE_CAL_Msk ) >> FUSES_DFLL48M_COARSE_CAL_Pos ;
121+ if (coarse == 0x3f ) {
122+ coarse = 0x1f ;
123+ }
124+ SYSCTRL -> DFLLVAL .reg = SYSCTRL_DFLLVAL_COARSE (coarse ) |
125+ SYSCTRL_DFLLVAL_FINE (512 );
126+
127+ SYSCTRL -> DFLLCTRL .reg = 0 ;
128+ while (!SYSCTRL -> PCLKSR .bit .DFLLRDY ) {}
129+ SYSCTRL -> DFLLCTRL .reg = SYSCTRL_DFLLCTRL_MODE |
130+ SYSCTRL_DFLLCTRL_ENABLE ;
131+ while (!SYSCTRL -> PCLKSR .bit .DFLLRDY ) {}
132+ while (GCLK -> STATUS .bit .SYNCBUSY ) {}
133+ }
134+
135+ static void init_clock_source_dfll48m_usb (void ) {
106136 SYSCTRL -> DFLLCTRL .reg = SYSCTRL_DFLLCTRL_ENABLE ;
107137 while (!SYSCTRL -> PCLKSR .bit .DFLLRDY ) {}
108138 SYSCTRL -> DFLLMUL .reg = SYSCTRL_DFLLMUL_CSTEP (1 ) |
109139 SYSCTRL_DFLLMUL_FSTEP (1 ) |
110140 SYSCTRL_DFLLMUL_MUL (48000 );
111141 uint32_t coarse = (* ((uint32_t * )FUSES_DFLL48M_COARSE_CAL_ADDR ) & FUSES_DFLL48M_COARSE_CAL_Msk ) >> FUSES_DFLL48M_COARSE_CAL_Pos ;
112- if (coarse == 0x3f )
142+ if (coarse == 0x3f ) {
113143 coarse = 0x1f ;
144+ }
145+ uint32_t fine = 512 ;
146+ #ifdef CALIBRATE_CRYSTALLESS
147+ // This is stored in an NVM page after the text and data storage but before
148+ // the optional file system. The first 16 bytes are the identifier for the
149+ // section.
150+ if (strcmp ((char * ) INTERNAL_CIRCUITPY_CONFIG_START_ADDR , "CIRCUITPYTHON1" ) == 0 ) {
151+ fine = ((uint16_t * ) INTERNAL_CIRCUITPY_CONFIG_START_ADDR )[8 ];
152+ }
153+ #endif
114154 SYSCTRL -> DFLLVAL .reg = SYSCTRL_DFLLVAL_COARSE (coarse ) |
115- SYSCTRL_DFLLVAL_FINE (512 );
155+ SYSCTRL_DFLLVAL_FINE (fine );
116156 SYSCTRL -> DFLLCTRL .reg = SYSCTRL_DFLLCTRL_CCDIS |
117157 SYSCTRL_DFLLCTRL_USBCRM |
118158 SYSCTRL_DFLLCTRL_MODE |
@@ -130,9 +170,16 @@ void clock_init(void)
130170 init_clock_source_osc32k ();
131171 }
132172
173+ if (board_has_crystal ()) {
174+ enable_clock_generator (3 , GCLK_GENCTRL_SRC_XOSC32K_Val , 1 );
175+ connect_gclk_to_peripheral (3 , GCLK_CLKCTRL_ID_DFLL48_Val );
176+ init_clock_source_dfll48m_xosc ();
177+ } else {
178+ init_clock_source_dfll48m_usb ();
179+ }
180+
133181 enable_clock_generator (0 , GCLK_GENCTRL_SRC_DFLL48M_Val , 1 );
134182 enable_clock_generator (1 , GCLK_GENCTRL_SRC_DFLL48M_Val , 150 );
135- init_clock_source_dfll48m ();
136183 if (board_has_crystal ()) {
137184 enable_clock_generator (2 , GCLK_GENCTRL_SRC_XOSC32K_Val , 32 );
138185 } else {
@@ -316,6 +363,41 @@ int clock_set_calibration(uint8_t type, uint8_t index, uint32_t val) {
316363 return -2 ; // calibration is read only
317364}
318365
366+ void save_usb_clock_calibration (void ) {
367+ #ifndef CALIBRATE_CRYSTALLESS
368+ return ;
369+ #endif
370+ // If we are on USB lets double check our fine calibration for the clock and
371+ // save the new value if its different enough.
372+ SYSCTRL -> DFLLSYNC .bit .READREQ = 1 ;
373+ uint16_t saved_calibration = 0x1ff ;
374+ if (strcmp ((char * ) INTERNAL_CIRCUITPY_CONFIG_START_ADDR , "CIRCUITPYTHON1" ) == 0 ) {
375+ saved_calibration = ((uint16_t * ) INTERNAL_CIRCUITPY_CONFIG_START_ADDR )[8 ];
376+ }
377+ while (SYSCTRL -> PCLKSR .bit .DFLLRDY == 0 ) {
378+ // TODO(tannewt): Run the mass storage stuff if this takes a while.
379+ }
380+ int16_t current_calibration = SYSCTRL -> DFLLVAL .bit .FINE ;
381+ if (abs (current_calibration - saved_calibration ) > 10 ) {
382+ // Copy the full internal config page to memory.
383+ uint8_t page_buffer [NVMCTRL_ROW_SIZE ];
384+ memcpy (page_buffer , (uint8_t * ) INTERNAL_CIRCUITPY_CONFIG_START_ADDR , NVMCTRL_ROW_SIZE );
385+
386+ // Modify it.
387+ memcpy (page_buffer , "CIRCUITPYTHON1" , 15 );
388+ // First 16 bytes (0-15) are ID. Little endian!
389+ page_buffer [16 ] = current_calibration & 0xff ;
390+ page_buffer [17 ] = current_calibration >> 8 ;
391+
392+ // Write it back.
393+ // We don't use features that use any advanced NVMCTRL features so we can fake the descriptor
394+ // whenever we need it instead of storing it long term.
395+ struct flash_descriptor desc ;
396+ desc .dev .hw = NVMCTRL ;
397+ flash_write (& desc , (uint32_t ) INTERNAL_CIRCUITPY_CONFIG_START_ADDR , page_buffer , NVMCTRL_ROW_SIZE );
398+ }
399+ }
400+
319401#ifdef SAMD21_EXPOSE_ALL_CLOCKS
320402CLOCK_SOURCE (XOSC );
321403CLOCK_SOURCE (GCLKIN );
0 commit comments