Skip to content

Commit 36fefb5

Browse files
authored
Implement pico_bootsel_via_double_reset with the POWMAN DOUBLE_TAP flag on RP2350. (#2083)
The existing RAM implementation does not work because asserting the RUN pin holds SRAM in power-down.
1 parent 90bfb53 commit 36fefb5

File tree

1 file changed

+62
-11
lines changed

1 file changed

+62
-11
lines changed

src/rp2_common/pico_bootsel_via_double_reset/pico_bootsel_via_double_reset.c

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
#include "pico/bootrom.h"
1414
#include "pico/binary_info.h"
1515

16+
#if !PICO_RP2040
17+
#include "hardware/structs/powman.h"
18+
#endif
19+
1620
// 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
1721
#ifndef PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS
1822
#define PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS 200
@@ -41,12 +45,62 @@
4145
bi_decl(bi_program_feature("double reset -> BOOTSEL"));
4246
#endif
4347

48+
#if PICO_RP2040
49+
50+
// RP2040 stores a token in RAM, which is retained over assertion of the RUN pin.
51+
4452
static const uint32_t magic_token[] = {
4553
0xf01681de, 0xbd729b29, 0xd359be7a,
4654
};
4755

4856
static uint32_t __uninitialized_ram(magic_location)[count_of(magic_token)];
4957

58+
static inline bool double_tap_flag_is_set(void) {
59+
for (uint i = 0; i < count_of(magic_token); i++) {
60+
if (magic_location[i] != magic_token[i]) {
61+
return false;
62+
}
63+
}
64+
return true;
65+
}
66+
67+
static inline void set_double_tap_flag(void) {
68+
for (uint i = 0; i < count_of(magic_token); i++) {
69+
magic_location[i] = magic_token[i];
70+
}
71+
}
72+
73+
static inline void clear_double_tap_flag(void) {
74+
magic_location[0] = 0;
75+
}
76+
77+
#else
78+
79+
// Newer microcontrollers have a purpose-made register which is retained over
80+
// RUN events, for detecting double-tap events. The ROM has built-in support
81+
// for this, but this library can also use the same hardware feature.
82+
// (Also, RAM is powered down when the RUN pin is asserted, so it's a bad
83+
// place to put the token!)
84+
//
85+
// Note if ROM support is also enabled (via DOUBLE_TAP in OTP BOOT_FLAGS) then
86+
// we never reach this point with the double tap flag still set. The window
87+
// is the sum of the delay added by this library and the delay added by the
88+
// ROM. It's not recommended to enable both, but it works.
89+
90+
static inline bool double_tap_flag_is_set(void) {
91+
return powman_hw->chip_reset & POWMAN_CHIP_RESET_DOUBLE_TAP_BITS;
92+
}
93+
94+
static inline void set_double_tap_flag(void) {
95+
hw_set_bits(&powman_hw->chip_reset, POWMAN_CHIP_RESET_DOUBLE_TAP_BITS);
96+
}
97+
98+
static inline void clear_double_tap_flag(void) {
99+
hw_clear_bits(&powman_hw->chip_reset, POWMAN_CHIP_RESET_DOUBLE_TAP_BITS);
100+
}
101+
102+
#endif
103+
50104
/* Check for double reset and enter BOOTSEL mode if detected
51105
*
52106
* 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)];
62116
* in place so that the second boot will go to the bootloader.
63117
*/
64118
static void __attribute__((constructor)) boot_double_tap_check(void) {
65-
for (uint i = 0; i < count_of(magic_token); i++) {
66-
if (magic_location[i] != magic_token[i]) {
67-
// Arm, wait, then disarm and continue booting
68-
for (i = 0; i < count_of(magic_token); i++) {
69-
magic_location[i] = magic_token[i];
70-
}
71-
busy_wait_us(PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS * 1000);
72-
magic_location[0] = 0;
73-
return;
74-
}
119+
if (!double_tap_flag_is_set()) {
120+
// Arm, wait, then disarm and continue booting
121+
set_double_tap_flag();
122+
busy_wait_us(PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS * 1000);
123+
clear_double_tap_flag();
124+
return;
75125
}
126+
76127
// Detected a double reset, so enter USB bootloader
77-
magic_location[0] = 0;
128+
clear_double_tap_flag();
78129
#ifdef PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED
79130
const uint32_t led_mask = 1u << PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED;
80131
#else

0 commit comments

Comments
 (0)