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. (#2083)

The existing RAM implementation does not work because asserting the RUN pin holds SRAM in power-down.
Wren6991 authored Nov 21, 2024
1 parent 90bfb53 commit 36fefb5
Showing 1 changed file with 62 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 36fefb5

Please sign in to comment.