Skip to content

Commit 8f7496c

Browse files
authored
Merge pull request #10231 from eightycc/issue-9710
Implement BOOTSEL button entry to safe mode for RP2.
2 parents afdb66b + c266993 commit 8f7496c

File tree

7 files changed

+101
-29
lines changed

7 files changed

+101
-29
lines changed

Diff for: ports/raspberrypi/mpconfigport.h

+5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@
3333

3434
#define CIRCUITPY_PROCESSOR_COUNT (2)
3535

36+
// For many RP2 boards BOOTSEL is not connected to a GPIO pin.
37+
#ifndef CIRCUITPY_BOOT_BUTTON
38+
#define CIRCUITPY_BOOT_BUTTON_NO_GPIO (1)
39+
#endif
40+
3641
#if CIRCUITPY_USB_HOST
3742
#define CIRCUITPY_USB_HOST_INSTANCE 1
3843
#endif

Diff for: ports/raspberrypi/supervisor/port.c

+57
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@
5757
#include "RP2350.h" // CMSIS
5858
#endif
5959

60+
#if CIRCUITPY_BOOT_BUTTON_NO_GPIO
61+
#include "hardware/gpio.h"
62+
#include "hardware/sync.h"
63+
#include "hardware/structs/ioqspi.h"
64+
#include "hardware/structs/sio.h"
65+
#endif
66+
6067
#include "supervisor/shared/serial.h"
6168

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

Diff for: py/circuitpy_mpconfig.h

+9
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,15 @@ void background_callback_run_all(void);
635635
#define CIRCUITPY_SAVES_PARTITION_SIZE 0
636636
#endif
637637

638+
// Boards that have a boot button connected to a GPIO pin should set
639+
// CIRCUITPY_BOOT_BUTTON_NO_GPIO to 1.
640+
#ifndef CIRCUITPY_BOOT_BUTTON_NO_GPIO
641+
#define CIRCUITPY_BOOT_BUTTON_NO_GPIO (0)
642+
#endif
643+
#if defined(CIRCUITPY_BOOT_BUTTON) && CIRCUITPY_BOOT_BUTTON_NO_GPIO
644+
#error "CIRCUITPY_BOOT_BUTTON and CIRCUITPY_BOOT_BUTTON_NO_GPIO are mutually exclusive"
645+
#endif
646+
638647
#if defined(__GNUC__) && !defined(__ZEPHYR__)
639648
#if __GNUC__ < CIRCUITPY_MIN_GCC_VERSION
640649
// (the 3 level scheme here is required to get expansion & stringization

Diff for: supervisor/port.h

+5
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);

Diff for: supervisor/shared/bluetooth/bluetooth.c

+3-14
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,13 @@
1010

1111
#include "shared-bindings/_bleio/__init__.h"
1212
#include "shared-bindings/_bleio/Adapter.h"
13-
#if defined(CIRCUITPY_BOOT_BUTTON)
14-
#include "shared-bindings/digitalio/DigitalInOut.h"
15-
#include "shared-bindings/time/__init__.h"
16-
#endif
1713
#include "shared-bindings/microcontroller/Processor.h"
1814
#include "shared-bindings/microcontroller/ResetReason.h"
1915
#include "shared-module/storage/__init__.h"
2016

2117
#include "common-hal/_bleio/__init__.h"
2218

19+
#include "supervisor/port.h"
2320
#include "supervisor/shared/serial.h"
2421
#include "supervisor/shared/status_leds.h"
2522
#include "supervisor/shared/tick.h"
@@ -238,18 +235,10 @@ void supervisor_bluetooth_init(void) {
238235
new_status_color(BLACK);
239236
}
240237
#endif
241-
// Init the boot button every time in case it is used for LEDs.
242-
#ifdef CIRCUITPY_BOOT_BUTTON
243-
digitalio_digitalinout_obj_t boot_button;
244-
common_hal_digitalio_digitalinout_construct(&boot_button, CIRCUITPY_BOOT_BUTTON);
245-
common_hal_digitalio_digitalinout_switch_to_input(&boot_button, PULL_UP);
246-
common_hal_time_delay_ms(1);
247-
bool button_pressed = !common_hal_digitalio_digitalinout_get_value(&boot_button);
248-
common_hal_digitalio_digitalinout_deinit(&boot_button);
249-
if (button_pressed) {
238+
if (port_boot_button_pressed()) {
250239
boot_in_discovery_mode = true;
240+
break;
251241
}
252-
#endif
253242
diff = supervisor_ticks_ms64() - start_ticks;
254243
}
255244
if (boot_in_discovery_mode) {

Diff for: supervisor/shared/port.c

+20
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)
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+
}

Diff for: supervisor/shared/safe_mode.c

+2-15
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
@@ -142,7 +129,7 @@ void print_safe_mode_message(safe_mode_t reason) {
142129
case SAFE_MODE_USER:
143130
#if defined(BOARD_USER_SAFE_MODE_ACTION)
144131
message = BOARD_USER_SAFE_MODE_ACTION;
145-
#elif defined(CIRCUITPY_BOOT_BUTTON)
132+
#elif defined(CIRCUITPY_BOOT_BUTTON) || CIRCUITPY_BOOT_BUTTON_NO_GPIO
146133
message = MP_ERROR_TEXT("You pressed the BOOT button at start up");
147134
#else
148135
message = MP_ERROR_TEXT("You pressed the reset button during boot.");

0 commit comments

Comments
 (0)