1 /*
2 * Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include "pico.h"
8
9 // Doesn't make any sense for a RAM only binary
10 #if !PICO_NO_FLASH
11
12 #include "pico/time.h"
13 #include "pico/bootrom.h"
14 #include "pico/binary_info.h"
15
16 #if !PICO_RP2040
17 #include "hardware/structs/powman.h"
18 #endif
19
20 // 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
21 #ifndef PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS
22 #define PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS 200
23 #endif
24
25 // PICO_CONFIG: PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED, Optionally define a pin to use as bootloader activity LED when BOOTSEL mode is entered via reset double tap, type=int, min=0, max=47 on RP2350B, 29 otherwise, group=pico_bootsel_via_double_reset
26
27 // PICO_CONFIG: PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED_ACTIVE_LOW, Whether pin used as bootloader activity LED when BOOTSEL mode is entered via reset double tap is active low. Not supported on RP2040, type=bool, default=0, group=pico_bootsel_via_double_reset
28 #ifndef PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED_ACTIVE_LOW
29 #define PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED_ACTIVE_LOW 0
30 #endif
31
32 // PICO_CONFIG: PICO_BOOTSEL_VIA_DOUBLE_RESET_INTERFACE_DISABLE_MASK, Optionally disable either the mass storage interface (bit 0) or the PICOBOOT interface (bit 1) when entering BOOTSEL mode via double reset, type=int, min=0, max=3, default=0, group=pico_bootsel_via_double_reset
33 #ifndef PICO_BOOTSEL_VIA_DOUBLE_RESET_INTERFACE_DISABLE_MASK
34 #define PICO_BOOTSEL_VIA_DOUBLE_RESET_INTERFACE_DISABLE_MASK 0u
35 #endif
36
37 /** \defgroup pico_bootsel_via_double_reset pico_bootsel_via_double_reset
38 *
39 * \brief Optional support to make fast double reset of the system enter BOOTSEL mode
40 *
41 * \brief When the 'pico_bootsel_via_double_reset' library is linked, a function is
42 * injected before main() which will detect when the system has been reset
43 * twice in quick succession, and enter the USB ROM bootloader (BOOTSEL mode)
44 * when this happens. This allows a double tap of a reset button on a
45 * development board to be used to enter the ROM bootloader, provided this
46 * library is always linked.
47 */
48
49 #if !PICO_NO_BI_BOOTSEL_VIA_DOUBLE_RESET
50 bi_decl(bi_program_feature("double reset -> BOOTSEL"));
51 #endif
52
53 #if PICO_RP2040
54
55 // RP2040 stores a token in RAM, which is retained over assertion of the RUN pin.
56
57 static const uint32_t magic_token[] = {
58 0xf01681de, 0xbd729b29, 0xd359be7a,
59 };
60
61 static uint32_t __uninitialized_ram(magic_location)[count_of(magic_token)];
62
double_tap_flag_is_set(void)63 static inline bool double_tap_flag_is_set(void) {
64 for (uint i = 0; i < count_of(magic_token); i++) {
65 if (magic_location[i] != magic_token[i]) {
66 return false;
67 }
68 }
69 return true;
70 }
71
set_double_tap_flag(void)72 static inline void set_double_tap_flag(void) {
73 for (uint i = 0; i < count_of(magic_token); i++) {
74 magic_location[i] = magic_token[i];
75 }
76 }
77
clear_double_tap_flag(void)78 static inline void clear_double_tap_flag(void) {
79 magic_location[0] = 0;
80 }
81
82 #else
83
84 // Newer microcontrollers have a purpose-made register which is retained over
85 // RUN events, for detecting double-tap events. The ROM has built-in support
86 // for this, but this library can also use the same hardware feature.
87 // (Also, RAM is powered down when the RUN pin is asserted, so it's a bad
88 // place to put the token!)
89 //
90 // Note if ROM support is also enabled (via DOUBLE_TAP in OTP BOOT_FLAGS) then
91 // we never reach this point with the double tap flag still set. The window
92 // is the sum of the delay added by this library and the delay added by the
93 // ROM. It's not recommended to enable both, but it works.
94
double_tap_flag_is_set(void)95 static inline bool double_tap_flag_is_set(void) {
96 return powman_hw->chip_reset & POWMAN_CHIP_RESET_DOUBLE_TAP_BITS;
97 }
98
set_double_tap_flag(void)99 static inline void set_double_tap_flag(void) {
100 hw_set_bits(&powman_hw->chip_reset, POWMAN_CHIP_RESET_DOUBLE_TAP_BITS);
101 }
102
clear_double_tap_flag(void)103 static inline void clear_double_tap_flag(void) {
104 hw_clear_bits(&powman_hw->chip_reset, POWMAN_CHIP_RESET_DOUBLE_TAP_BITS);
105 }
106
107 #endif
108
109 /* Check for double reset and enter BOOTSEL mode if detected
110 *
111 * This function is registered to run automatically before main(). The
112 * algorithm is:
113 *
114 * 1. Check for magic token in memory; enter BOOTSEL mode if found.
115 * 2. Initialise that memory with that magic token.
116 * 3. Do nothing for a short while (few hundred ms).
117 * 4. Clear the magic token.
118 * 5. Continue with normal boot.
119 *
120 * Resetting the device twice quickly will interrupt step 3, leaving the token
121 * in place so that the second boot will go to the bootloader.
122 */
boot_double_tap_check(void)123 static void __attribute__((constructor)) boot_double_tap_check(void) {
124 if (!double_tap_flag_is_set()) {
125 // Arm, wait, then disarm and continue booting
126 set_double_tap_flag();
127 busy_wait_us(PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS * 1000);
128 clear_double_tap_flag();
129 return;
130 }
131
132 // Detected a double reset, so enter USB bootloader
133 clear_double_tap_flag();
134 #ifdef PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED
135 const int led = PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED;
136 #else
137 const int led = -1;
138 #endif
139 rom_reset_usb_boot_extra(
140 led,
141 PICO_BOOTSEL_VIA_DOUBLE_RESET_INTERFACE_DISABLE_MASK,
142 PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED_ACTIVE_LOW
143 );
144 }
145
146 #endif
147