Skip to content

Commit

Permalink
Implement pico_bootsel_via_double_reset with the POWMAN DOUBLE_TAP fl…
Browse files Browse the repository at this point in the history
…ag on RP2350.

The existing RAM implementation does not work because asserting the RUN pin holds SRAM in power-down.
  • Loading branch information
Wren6991 committed Nov 21, 2024
1 parent f3459a4 commit 1281507
Showing 1 changed file with 62 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 1281507

Please sign in to comment.