Skip to content

Commit 3c6056f

Browse files
committed
Implement BOOTSEL button entry to safe mode for RP2.
1 parent ad73d0b commit 3c6056f

File tree

5 files changed

+86
-14
lines changed

5 files changed

+86
-14
lines changed

ports/raspberrypi/mpconfigport.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333

3434
#define CIRCUITPY_PROCESSOR_COUNT (2)
3535

36+
// For many RP2 boards BOOTSEL is not connected to a GPIO pin.
37+
#define CIRCUITPY_BOOT_BUTTON (1)
38+
3639
#if CIRCUITPY_USB_HOST
3740
#define CIRCUITPY_USB_HOST_INSTANCE 1
3841
#endif

ports/raspberrypi/supervisor/port.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@
5656
#include "RP2350.h" // CMSIS
5757
#endif
5858

59+
#ifdef CIRCUITPY_BOOT_BUTTON
60+
#include "hardware/gpio.h"
61+
#include "hardware/sync.h"
62+
#include "hardware/structs/ioqspi.h"
63+
#include "hardware/structs/sio.h"
64+
#endif
65+
5966
#include "supervisor/shared/serial.h"
6067

6168
#include "tusb.h"
@@ -576,3 +583,53 @@ void port_boot_info(void) {
576583
mp_printf(&mp_plat_print, "\n");
577584
#endif
578585
}
586+
587+
#if defined(CIRCUITPY_BOOT_BUTTON)
588+
bool __no_inline_not_in_flash_func(port_boot_button_pressed)(void) {
589+
// Sense the state of the boot button. Because this function
590+
// disables flash, it cannot be safely called once the second
591+
// core has been started. When the BOOTSEL button is sensed as
592+
// pressed, return is delayed until the button is released and
593+
// a delay has passed in order to debounce the button.
594+
const uint32_t CS_PIN_INDEX = 1;
595+
#if defined(PICO_RP2040)
596+
const uint32_t CS_BIT = 1u << 1;
597+
#else
598+
const uint32_t CS_BIT = SIO_GPIO_HI_IN_QSPI_CSN_BITS;
599+
#endif
600+
uint32_t int_state = save_and_disable_interrupts();
601+
// Wait for any outstanding XIP activity to finish. Flash
602+
// must be quiescent before disabling the chip select. Since
603+
// there's no XIP busy indication we can test, we delay a
604+
// generous 5 ms to allow any XIP activity to finish.
605+
busy_wait_us(5000);
606+
// Float the flash chip select pin. The line will HI-Z due to
607+
// the external 10K pull-up resistor.
608+
hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl,
609+
GPIO_OVERRIDE_LOW << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB,
610+
IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
611+
// Delay 100 us to allow the CS line to stabilize. If BOOTSEL is
612+
// pressed, the line will be pulled low by the button and its
613+
// 1K external resistor to ground.
614+
busy_wait_us(100);
615+
bool button_pressed = !(sio_hw->gpio_hi_in & CS_BIT);
616+
// Wait for the button to be released.
617+
if (button_pressed) {
618+
while (!(sio_hw->gpio_hi_in & CS_BIT)) {
619+
tight_loop_contents();
620+
}
621+
// Wait for 50 ms to debounce the button.
622+
busy_wait_us(50000);
623+
}
624+
// Restore the flash chip select pin to its original state.
625+
hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl,
626+
GPIO_OVERRIDE_NORMAL << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB,
627+
IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
628+
// Delay 5 ms to allow the flash chip to re-enable and for the
629+
// flash CS pin to stabilize.
630+
busy_wait_us(5000);
631+
// Restore the interrupt state.
632+
restore_interrupts(int_state);
633+
return button_pressed;
634+
}
635+
#endif

supervisor/port.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,8 @@ void port_boot_info(void);
108108
// Some ports want to mark additional pointers as gc roots.
109109
// A default weak implementation is provided that does nothing.
110110
void port_gc_collect(void);
111+
112+
// Most ports that implement CIRCUITPY_BOOT_BUTTON use a generic version of
113+
// this function to sense the button. Ports that need to can override this
114+
// function to provide their own implementation.
115+
bool port_boot_button_pressed(void);

supervisor/shared/port.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212

1313
#include "lib/tlsf/tlsf.h"
1414

15+
#ifdef CIRCUITPY_BOOT_BUTTON
16+
#include "shared-bindings/digitalio/DigitalInOut.h"
17+
#include "shared-bindings/time/__init__.h"
18+
#endif
19+
1520
static tlsf_t heap;
1621

1722
MP_WEAK void port_wake_main_task(void) {
@@ -60,3 +65,18 @@ MP_WEAK size_t port_heap_get_largest_free_size(void) {
6065
// IDF does this. Not sure why.
6166
return tlsf_fit_size(heap, max_size);
6267
}
68+
69+
MP_WEAK bool port_boot_button_pressed(void) {
70+
#if defined(CIRCUITPY_BOOT_BUTTON) && CIRCUITPY_BOOT_BUTTON != 1
71+
// Init/deinit the boot button every time in case it is used for LEDs.
72+
digitalio_digitalinout_obj_t boot_button;
73+
common_hal_digitalio_digitalinout_construct(&boot_button, CIRCUITPY_BOOT_BUTTON);
74+
common_hal_digitalio_digitalinout_switch_to_input(&boot_button, PULL_UP);
75+
common_hal_time_delay_ms(1);
76+
bool button_pressed = !common_hal_digitalio_digitalinout_get_value(&boot_button);
77+
common_hal_digitalio_digitalinout_deinit(&boot_button);
78+
return button_pressed;
79+
#else
80+
return false;
81+
#endif
82+
}

supervisor/shared/safe_mode.c

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@
88

99
#include "mphalport.h"
1010

11-
#if defined(CIRCUITPY_BOOT_BUTTON)
12-
#include "shared-bindings/digitalio/DigitalInOut.h"
13-
#include "shared-bindings/time/__init__.h"
14-
#endif
1511
#include "shared-bindings/microcontroller/Processor.h"
1612
#include "shared-bindings/microcontroller/ResetReason.h"
1713

@@ -78,19 +74,10 @@ safe_mode_t wait_for_safe_mode_reset(void) {
7874
new_status_color(BLACK);
7975
}
8076
#endif
81-
// Init the boot button every time in case it is used for LEDs.
82-
#ifdef CIRCUITPY_BOOT_BUTTON
83-
digitalio_digitalinout_obj_t boot_button;
84-
common_hal_digitalio_digitalinout_construct(&boot_button, CIRCUITPY_BOOT_BUTTON);
85-
common_hal_digitalio_digitalinout_switch_to_input(&boot_button, PULL_UP);
86-
common_hal_time_delay_ms(1);
87-
bool button_pressed = !common_hal_digitalio_digitalinout_get_value(&boot_button);
88-
common_hal_digitalio_digitalinout_deinit(&boot_button);
89-
if (button_pressed) {
77+
if (port_boot_button_pressed()) {
9078
boot_in_safe_mode = true;
9179
break;
9280
}
93-
#endif
9481
diff = supervisor_ticks_ms64() - start_ticks;
9582
}
9683
#if CIRCUITPY_STATUS_LED

0 commit comments

Comments
 (0)