diff --git a/ports/atmel-samd/peripherals/clocks.h b/ports/atmel-samd/peripherals/clocks.h index 78b2e2e3d5..22c166d3e2 100644 --- a/ports/atmel-samd/peripherals/clocks.h +++ b/ports/atmel-samd/peripherals/clocks.h @@ -70,5 +70,6 @@ bool clock_get_parent(uint8_t type, uint8_t index, uint8_t *p_type, uint8_t *p_i uint32_t clock_get_frequency(uint8_t type, uint8_t index); uint32_t clock_get_calibration(uint8_t type, uint8_t index); int clock_set_calibration(uint8_t type, uint8_t index, uint32_t val); +void save_usb_clock_calibration(void); #endif // MICROPY_INCLUDED_ATMEL_SAMD_CLOCKS_H diff --git a/ports/atmel-samd/peripherals/samd21/clocks.c b/ports/atmel-samd/peripherals/samd21/clocks.c index aad251a84c..51b7ad993d 100644 --- a/ports/atmel-samd/peripherals/samd21/clocks.c +++ b/ports/atmel-samd/peripherals/samd21/clocks.c @@ -24,15 +24,24 @@ * THE SOFTWARE. */ +#include +#include + #include "peripherals/clocks.h" -#include "hpl_gclk_config.h" +#include "hal/include/hal_flash.h" #include "bindings/samd/Clock.h" #include "shared-bindings/microcontroller/__init__.h" #include "py/runtime.h" +#ifdef EXPRESS_BOARD +#define INTERNAL_CIRCUITPY_CONFIG_START_ADDR (0x00040000 - NVMCTRL_ROW_SIZE - CIRCUITPY_INTERNAL_NVM_SIZE) +#else +#define INTERNAL_CIRCUITPY_CONFIG_START_ADDR (0x00040000 - 0x010000 - NVMCTRL_ROW_SIZE - CIRCUITPY_INTERNAL_NVM_SIZE) +#endif + bool gclk_enabled(uint8_t gclk) { common_hal_mcu_disable_interrupts(); // 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) { while (!SYSCTRL->PCLKSR.bit.XOSC32KRDY) {} } -static void init_clock_source_dfll48m(void) { +static void init_clock_source_dfll48m_xosc(void) { + SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_ENABLE; + while (!SYSCTRL->PCLKSR.bit.DFLLRDY) {} + SYSCTRL->DFLLMUL.reg = SYSCTRL_DFLLMUL_CSTEP(0x1f / 4) | + SYSCTRL_DFLLMUL_FSTEP(0xff / 4) | + SYSCTRL_DFLLMUL_MUL(48000000 / 32768); + uint32_t coarse = (*((uint32_t *)FUSES_DFLL48M_COARSE_CAL_ADDR) & FUSES_DFLL48M_COARSE_CAL_Msk) >> FUSES_DFLL48M_COARSE_CAL_Pos; + if (coarse == 0x3f) { + coarse = 0x1f; + } + SYSCTRL->DFLLVAL.reg = SYSCTRL_DFLLVAL_COARSE(coarse) | + SYSCTRL_DFLLVAL_FINE(512); + + SYSCTRL->DFLLCTRL.reg = 0; + while (!SYSCTRL->PCLKSR.bit.DFLLRDY) {} + SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_MODE | + SYSCTRL_DFLLCTRL_ENABLE; + while (!SYSCTRL->PCLKSR.bit.DFLLRDY) {} + while (GCLK->STATUS.bit.SYNCBUSY) {} +} + +static void init_clock_source_dfll48m_usb(void) { SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_ENABLE; while (!SYSCTRL->PCLKSR.bit.DFLLRDY) {} SYSCTRL->DFLLMUL.reg = SYSCTRL_DFLLMUL_CSTEP(1) | SYSCTRL_DFLLMUL_FSTEP(1) | SYSCTRL_DFLLMUL_MUL(48000); uint32_t coarse = (*((uint32_t *)FUSES_DFLL48M_COARSE_CAL_ADDR) & FUSES_DFLL48M_COARSE_CAL_Msk) >> FUSES_DFLL48M_COARSE_CAL_Pos; - if (coarse == 0x3f) + if (coarse == 0x3f) { coarse = 0x1f; + } + uint32_t fine = 512; + #ifdef CALIBRATE_CRYSTALLESS + // This is stored in an NVM page after the text and data storage but before + // the optional file system. The first 16 bytes are the identifier for the + // section. + if (strcmp((char*) INTERNAL_CIRCUITPY_CONFIG_START_ADDR, "CIRCUITPYTHON1") == 0) { + fine = ((uint16_t *) INTERNAL_CIRCUITPY_CONFIG_START_ADDR)[8]; + } + #endif SYSCTRL->DFLLVAL.reg = SYSCTRL_DFLLVAL_COARSE(coarse) | - SYSCTRL_DFLLVAL_FINE(512); + SYSCTRL_DFLLVAL_FINE(fine); SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_CCDIS | SYSCTRL_DFLLCTRL_USBCRM | SYSCTRL_DFLLCTRL_MODE | @@ -130,9 +170,16 @@ void clock_init(void) init_clock_source_osc32k(); } + if (board_has_crystal()) { + enable_clock_generator(3, GCLK_GENCTRL_SRC_XOSC32K_Val, 1); + connect_gclk_to_peripheral(3, GCLK_CLKCTRL_ID_DFLL48_Val); + init_clock_source_dfll48m_xosc(); + } else { + init_clock_source_dfll48m_usb(); + } + enable_clock_generator(0, GCLK_GENCTRL_SRC_DFLL48M_Val, 1); enable_clock_generator(1, GCLK_GENCTRL_SRC_DFLL48M_Val, 150); - init_clock_source_dfll48m(); if (board_has_crystal()) { enable_clock_generator(2, GCLK_GENCTRL_SRC_XOSC32K_Val, 32); } else { @@ -316,6 +363,41 @@ int clock_set_calibration(uint8_t type, uint8_t index, uint32_t val) { return -2; // calibration is read only } +void save_usb_clock_calibration(void) { + #ifndef CALIBRATE_CRYSTALLESS + return; + #endif + // If we are on USB lets double check our fine calibration for the clock and + // save the new value if its different enough. + SYSCTRL->DFLLSYNC.bit.READREQ = 1; + uint16_t saved_calibration = 0x1ff; + if (strcmp((char*) INTERNAL_CIRCUITPY_CONFIG_START_ADDR, "CIRCUITPYTHON1") == 0) { + saved_calibration = ((uint16_t *) INTERNAL_CIRCUITPY_CONFIG_START_ADDR)[8]; + } + while (SYSCTRL->PCLKSR.bit.DFLLRDY == 0) { + // TODO(tannewt): Run the mass storage stuff if this takes a while. + } + int16_t current_calibration = SYSCTRL->DFLLVAL.bit.FINE; + if (abs(current_calibration - saved_calibration) > 10) { + // Copy the full internal config page to memory. + uint8_t page_buffer[NVMCTRL_ROW_SIZE]; + memcpy(page_buffer, (uint8_t*) INTERNAL_CIRCUITPY_CONFIG_START_ADDR, NVMCTRL_ROW_SIZE); + + // Modify it. + memcpy(page_buffer, "CIRCUITPYTHON1", 15); + // First 16 bytes (0-15) are ID. Little endian! + page_buffer[16] = current_calibration & 0xff; + page_buffer[17] = current_calibration >> 8; + + // Write it back. + // We don't use features that use any advanced NVMCTRL features so we can fake the descriptor + // whenever we need it instead of storing it long term. + struct flash_descriptor desc; + desc.dev.hw = NVMCTRL; + flash_write(&desc, (uint32_t) INTERNAL_CIRCUITPY_CONFIG_START_ADDR, page_buffer, NVMCTRL_ROW_SIZE); + } +} + #ifdef SAMD21_EXPOSE_ALL_CLOCKS CLOCK_SOURCE(XOSC); CLOCK_SOURCE(GCLKIN); diff --git a/ports/atmel-samd/peripherals/samd51/clocks.c b/ports/atmel-samd/peripherals/samd51/clocks.c index 29f9cb99cb..37468a3f7b 100644 --- a/ports/atmel-samd/peripherals/samd51/clocks.c +++ b/ports/atmel-samd/peripherals/samd51/clocks.c @@ -123,6 +123,9 @@ void clock_init(void) { enable_clock_generator_sync(5, GCLK_GENCTRL_SRC_DFLL_Val, 24, false); init_clock_source_dpll0(); + + // Do this after all static clock init so that they aren't used dynamically. + init_dynamic_clocks(); } static bool clk_enabled(uint8_t clk) { @@ -203,6 +206,7 @@ static uint32_t dpll_get_frequency(uint8_t index) { break; case 0x2: // XOSC0 case 0x3: // XOSC1 + default: return 0; // unknown } @@ -335,6 +339,10 @@ int clock_set_calibration(uint8_t type, uint8_t index, uint32_t val) { return -2; } + +void save_usb_clock_calibration(void) { +} + #include #include #include diff --git a/ports/atmel-samd/supervisor/filesystem.c b/ports/atmel-samd/supervisor/filesystem.c index 2ba29f279f..8a6010951f 100644 --- a/ports/atmel-samd/supervisor/filesystem.c +++ b/ports/atmel-samd/supervisor/filesystem.c @@ -32,13 +32,6 @@ #include "flash_api.h" -#ifdef EXPRESS_BOARD -// #include "common-hal/touchio/TouchIn.h" -#define INTERNAL_CIRCUITPY_CONFIG_START_ADDR (0x00040000 - 0x100 - CIRCUITPY_INTERNAL_NVM_SIZE) -#else -#define INTERNAL_CIRCUITPY_CONFIG_START_ADDR (0x00040000 - 0x010000 - 0x100 - CIRCUITPY_INTERNAL_NVM_SIZE) -#endif - fs_user_mount_t fs_user_mount_flash; mp_vfs_mount_t mp_vfs_mount_flash; diff --git a/ports/atmel-samd/supervisor/port.c b/ports/atmel-samd/supervisor/port.c index e815d0f1ec..3e8d5bcc76 100644 --- a/ports/atmel-samd/supervisor/port.c +++ b/ports/atmel-samd/supervisor/port.c @@ -62,6 +62,7 @@ #include "peripherals/dma.h" #include "shared-bindings/rtc/__init__.h" #include "tick.h" +#include "usb.h" #ifdef CIRCUITPY_GAMEPAD_TICKS #include "shared-module/gamepad/__init__.h" @@ -184,7 +185,6 @@ safe_mode_t port_init(void) { _pm_init(); #endif clock_init(); - init_dynamic_clocks(); board_init(); @@ -270,16 +270,6 @@ void reset_port(void) { reset_all_pins(); - // Set up debugging pins after reset_all_pins(). - - // Uncomment to init PIN_PA17 for debugging. - // struct port_config pin_conf; - // port_get_config_defaults(&pin_conf); - // - // pin_conf.direction = PORT_PIN_DIR_OUTPUT; - // port_pin_set_config(MICROPY_HW_LED1, &pin_conf); - // port_pin_set_output_level(MICROPY_HW_LED1, false); - // Output clocks for debugging. // not supported by SAMD51G; uncomment for SAMD51J or update for 51G // #ifdef SAMD51 @@ -291,52 +281,9 @@ void reset_port(void) { usb_hid_reset(); -// #ifdef CALIBRATE_CRYSTALLESS -// // If we are on USB lets double check our fine calibration for the clock and -// // save the new value if its different enough. -// if (mp_msc_enabled) { -// SYSCTRL->DFLLSYNC.bit.READREQ = 1; -// uint16_t saved_calibration = 0x1ff; -// if (strcmp((char*) INTERNAL_CIRCUITPY_CONFIG_START_ADDR, "CIRCUITPYTHON1") == 0) { -// saved_calibration = ((uint16_t *) INTERNAL_CIRCUITPY_CONFIG_START_ADDR)[8]; -// } -// while (SYSCTRL->PCLKSR.bit.DFLLRDY == 0) { -// // TODO(tannewt): Run the mass storage stuff if this takes a while. -// } -// int16_t current_calibration = SYSCTRL->DFLLVAL.bit.FINE; -// if (abs(current_calibration - saved_calibration) > 10) { -// enum status_code error_code; -// uint8_t page_buffer[NVMCTRL_ROW_SIZE]; -// for (int i = 0; i < NVMCTRL_ROW_PAGES; i++) { -// do -// { -// error_code = nvm_read_buffer(INTERNAL_CIRCUITPY_CONFIG_START_ADDR + i * NVMCTRL_PAGE_SIZE, -// page_buffer + i * NVMCTRL_PAGE_SIZE, -// NVMCTRL_PAGE_SIZE); -// } while (error_code == STATUS_BUSY); -// } -// // If this is the first write, include the header. -// if (strcmp((char*) page_buffer, "CIRCUITPYTHON1") != 0) { -// memcpy(page_buffer, "CIRCUITPYTHON1", 15); -// } -// // First 16 bytes (0-15) are ID. Little endian! -// page_buffer[16] = current_calibration & 0xff; -// page_buffer[17] = current_calibration >> 8; -// do -// { -// error_code = nvm_erase_row(INTERNAL_CIRCUITPY_CONFIG_START_ADDR); -// } while (error_code == STATUS_BUSY); -// for (int i = 0; i < NVMCTRL_ROW_PAGES; i++) { -// do -// { -// error_code = nvm_write_buffer(INTERNAL_CIRCUITPY_CONFIG_START_ADDR + i * NVMCTRL_PAGE_SIZE, -// page_buffer + i * NVMCTRL_PAGE_SIZE, -// NVMCTRL_PAGE_SIZE); -// } while (error_code == STATUS_BUSY); -// } -// } -// } -// #endif + if (usb_connected()) { + save_usb_clock_calibration(); + } } /**