24
24
* THE SOFTWARE.
25
25
*/
26
26
27
+ #include <string.h>
28
+ #include <stdlib.h>
29
+
27
30
#include "peripherals/clocks.h"
28
31
29
- #include "hpl_gclk_config .h"
32
+ #include "hal/include/hal_flash .h"
30
33
31
34
#include "bindings/samd/Clock.h"
32
35
#include "shared-bindings/microcontroller/__init__.h"
33
36
34
37
#include "py/runtime.h"
35
38
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
+
36
45
bool gclk_enabled (uint8_t gclk ) {
37
46
common_hal_mcu_disable_interrupts ();
38
47
// 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) {
102
111
while (!SYSCTRL -> PCLKSR .bit .XOSC32KRDY ) {}
103
112
}
104
113
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 ) {
106
136
SYSCTRL -> DFLLCTRL .reg = SYSCTRL_DFLLCTRL_ENABLE ;
107
137
while (!SYSCTRL -> PCLKSR .bit .DFLLRDY ) {}
108
138
SYSCTRL -> DFLLMUL .reg = SYSCTRL_DFLLMUL_CSTEP (1 ) |
109
139
SYSCTRL_DFLLMUL_FSTEP (1 ) |
110
140
SYSCTRL_DFLLMUL_MUL (48000 );
111
141
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 ) {
113
143
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
114
154
SYSCTRL -> DFLLVAL .reg = SYSCTRL_DFLLVAL_COARSE (coarse ) |
115
- SYSCTRL_DFLLVAL_FINE (512 );
155
+ SYSCTRL_DFLLVAL_FINE (fine );
116
156
SYSCTRL -> DFLLCTRL .reg = SYSCTRL_DFLLCTRL_CCDIS |
117
157
SYSCTRL_DFLLCTRL_USBCRM |
118
158
SYSCTRL_DFLLCTRL_MODE |
@@ -130,9 +170,16 @@ void clock_init(void)
130
170
init_clock_source_osc32k ();
131
171
}
132
172
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
+
133
181
enable_clock_generator (0 , GCLK_GENCTRL_SRC_DFLL48M_Val , 1 );
134
182
enable_clock_generator (1 , GCLK_GENCTRL_SRC_DFLL48M_Val , 150 );
135
- init_clock_source_dfll48m ();
136
183
if (board_has_crystal ()) {
137
184
enable_clock_generator (2 , GCLK_GENCTRL_SRC_XOSC32K_Val , 32 );
138
185
} else {
@@ -316,6 +363,41 @@ int clock_set_calibration(uint8_t type, uint8_t index, uint32_t val) {
316
363
return -2 ; // calibration is read only
317
364
}
318
365
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
+
319
401
#ifdef SAMD21_EXPOSE_ALL_CLOCKS
320
402
CLOCK_SOURCE (XOSC );
321
403
CLOCK_SOURCE (GCLKIN );
0 commit comments