diff --git a/src/rp2_common/pico_bootsel_via_double_reset/pico_bootsel_via_double_reset.c b/src/rp2_common/pico_bootsel_via_double_reset/pico_bootsel_via_double_reset.c index 49aa4bad8..1c1947eac 100644 --- a/src/rp2_common/pico_bootsel_via_double_reset/pico_bootsel_via_double_reset.c +++ b/src/rp2_common/pico_bootsel_via_double_reset/pico_bootsel_via_double_reset.c @@ -13,6 +13,10 @@ #include "pico/bootrom.h" #include "pico/binary_info.h" +#if !PICO_RP2040 +#include "hardware/structs/powman.h" +#endif + // PICO_CONFIG: PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS, Window of opportunity for a second press of a reset button to enter BOOTSEL mode (milliseconds), type=int, default=200, group=pico_bootsel_via_double_reset #ifndef PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS #define PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS 200 @@ -41,12 +45,62 @@ bi_decl(bi_program_feature("double reset -> BOOTSEL")); #endif +#if PICO_RP2040 + +// RP2040 stores a token in RAM, which is retained over assertion of the RUN pin. + static const uint32_t magic_token[] = { 0xf01681de, 0xbd729b29, 0xd359be7a, }; static uint32_t __uninitialized_ram(magic_location)[count_of(magic_token)]; +static inline bool double_tap_flag_is_set(void) { + for (uint i = 0; i < count_of(magic_token); i++) { + if (magic_location[i] != magic_token[i]) { + return false; + } + } + return true; +} + +static inline void set_double_tap_flag(void) { + for (uint i = 0; i < count_of(magic_token); i++) { + magic_location[i] = magic_token[i]; + } +} + +static inline void clear_double_tap_flag(void) { + magic_location[0] = 0; +} + +#else + +// Newer microcontrollers have a purpose-made register which is retained over +// RUN events, for detecting double-tap events. The ROM has built-in support +// for this, but this library can also use the same hardware feature. +// (Also, RAM is powered down when the RUN pin is asserted, so it's a bad +// place to put the token!) +// +// Note if ROM support is also enabled (via DOUBLE_TAP in OTP BOOT_FLAGS) then +// we never reach this point with the double tap flag still set. The window +// is the sum of the delay added by this library and the delay added by the +// ROM. It's not recommended to enable both, but it works. + +static inline bool double_tap_flag_is_set(void) { + return powman_hw->chip_reset & POWMAN_CHIP_RESET_DOUBLE_TAP_BITS; +} + +static inline void set_double_tap_flag(void) { + hw_set_bits(&powman_hw->chip_reset, POWMAN_CHIP_RESET_DOUBLE_TAP_BITS); +} + +static inline void clear_double_tap_flag(void) { + hw_clear_bits(&powman_hw->chip_reset, POWMAN_CHIP_RESET_DOUBLE_TAP_BITS); +} + +#endif + /* Check for double reset and enter BOOTSEL mode if detected * * This function is registered to run automatically before main(). The @@ -62,19 +116,16 @@ static uint32_t __uninitialized_ram(magic_location)[count_of(magic_token)]; * in place so that the second boot will go to the bootloader. */ static void __attribute__((constructor)) boot_double_tap_check(void) { - for (uint i = 0; i < count_of(magic_token); i++) { - if (magic_location[i] != magic_token[i]) { - // Arm, wait, then disarm and continue booting - for (i = 0; i < count_of(magic_token); i++) { - magic_location[i] = magic_token[i]; - } - busy_wait_us(PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS * 1000); - magic_location[0] = 0; - return; - } + if (!double_tap_flag_is_set()) { + // Arm, wait, then disarm and continue booting + set_double_tap_flag(); + busy_wait_us(PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS * 1000); + clear_double_tap_flag(); + return; } + // Detected a double reset, so enter USB bootloader - magic_location[0] = 0; + clear_double_tap_flag(); #ifdef PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED const uint32_t led_mask = 1u << PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED; #else