diff --git a/ports/raspberrypi/mpconfigport.h b/ports/raspberrypi/mpconfigport.h index 1181517fdf9a0..aa20cd90b8378 100644 --- a/ports/raspberrypi/mpconfigport.h +++ b/ports/raspberrypi/mpconfigport.h @@ -33,6 +33,11 @@ #define CIRCUITPY_PROCESSOR_COUNT (2) +// For many RP2 boards BOOTSEL is not connected to a GPIO pin. +#ifndef CIRCUITPY_BOOT_BUTTON +#define CIRCUITPY_BOOT_BUTTON_NO_GPIO (1) +#endif + #if CIRCUITPY_USB_HOST #define CIRCUITPY_USB_HOST_INSTANCE 1 #endif diff --git a/ports/raspberrypi/supervisor/port.c b/ports/raspberrypi/supervisor/port.c index 7514b4e6ad4aa..7a929b866af3d 100644 --- a/ports/raspberrypi/supervisor/port.c +++ b/ports/raspberrypi/supervisor/port.c @@ -56,6 +56,13 @@ #include "RP2350.h" // CMSIS #endif +#if CIRCUITPY_BOOT_BUTTON_NO_GPIO +#include "hardware/gpio.h" +#include "hardware/sync.h" +#include "hardware/structs/ioqspi.h" +#include "hardware/structs/sio.h" +#endif + #include "supervisor/shared/serial.h" #include "tusb.h" @@ -576,3 +583,53 @@ void port_boot_info(void) { mp_printf(&mp_plat_print, "\n"); #endif } + +#if CIRCUITPY_BOOT_BUTTON_NO_GPIO +bool __no_inline_not_in_flash_func(port_boot_button_pressed)(void) { + // Sense the state of the boot button. Because this function + // disables flash, it cannot be safely called once the second + // core has been started. When the BOOTSEL button is sensed as + // pressed, return is delayed until the button is released and + // a delay has passed in order to debounce the button. + const uint32_t CS_PIN_INDEX = 1; + #if defined(PICO_RP2040) + const uint32_t CS_BIT = 1u << 1; + #else + const uint32_t CS_BIT = SIO_GPIO_HI_IN_QSPI_CSN_BITS; + #endif + uint32_t int_state = save_and_disable_interrupts(); + // Wait for any outstanding XIP activity to finish. Flash + // must be quiescent before disabling the chip select. Since + // there's no XIP busy indication we can test, we delay a + // generous 5 ms to allow any XIP activity to finish. + busy_wait_us(5000); + // Float the flash chip select pin. The line will HI-Z due to + // the external 10K pull-up resistor. + hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl, + GPIO_OVERRIDE_LOW << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB, + IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS); + // Delay 100 us to allow the CS line to stabilize. If BOOTSEL is + // pressed, the line will be pulled low by the button and its + // 1K external resistor to ground. + busy_wait_us(100); + bool button_pressed = !(sio_hw->gpio_hi_in & CS_BIT); + // Wait for the button to be released. + if (button_pressed) { + while (!(sio_hw->gpio_hi_in & CS_BIT)) { + tight_loop_contents(); + } + // Wait for 50 ms to debounce the button. + busy_wait_us(50000); + } + // Restore the flash chip select pin to its original state. + hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl, + GPIO_OVERRIDE_NORMAL << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB, + IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS); + // Delay 5 ms to allow the flash chip to re-enable and for the + // flash CS pin to stabilize. + busy_wait_us(5000); + // Restore the interrupt state. + restore_interrupts(int_state); + return button_pressed; +} +#endif diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 42e6f1841a445..836336a02a002 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -635,6 +635,15 @@ void background_callback_run_all(void); #define CIRCUITPY_SAVES_PARTITION_SIZE 0 #endif +// Boards that have a boot button connected to a GPIO pin should set +// CIRCUITPY_BOOT_BUTTON_NO_GPIO to 1. +#ifndef CIRCUITPY_BOOT_BUTTON_NO_GPIO +#define CIRCUITPY_BOOT_BUTTON_NO_GPIO (0) +#endif +#if defined(CIRCUITPY_BOOT_BUTTON) && CIRCUITPY_BOOT_BUTTON_NO_GPIO +#error "CIRCUITPY_BOOT_BUTTON and CIRCUITPY_BOOT_BUTTON_NO_GPIO are mutually exclusive" +#endif + #if defined(__GNUC__) && !defined(__ZEPHYR__) #if __GNUC__ < CIRCUITPY_MIN_GCC_VERSION // (the 3 level scheme here is required to get expansion & stringization diff --git a/supervisor/port.h b/supervisor/port.h index 29c071515c133..e38b5c49406ae 100644 --- a/supervisor/port.h +++ b/supervisor/port.h @@ -108,3 +108,8 @@ void port_boot_info(void); // Some ports want to mark additional pointers as gc roots. // A default weak implementation is provided that does nothing. void port_gc_collect(void); + +// Most ports that implement CIRCUITPY_BOOT_BUTTON use a generic version of +// this function to sense the button. Ports that need to can override this +// function to provide their own implementation. +bool port_boot_button_pressed(void); diff --git a/supervisor/shared/bluetooth/bluetooth.c b/supervisor/shared/bluetooth/bluetooth.c index 15e472148f67e..73c05139eeea3 100644 --- a/supervisor/shared/bluetooth/bluetooth.c +++ b/supervisor/shared/bluetooth/bluetooth.c @@ -10,16 +10,13 @@ #include "shared-bindings/_bleio/__init__.h" #include "shared-bindings/_bleio/Adapter.h" -#if defined(CIRCUITPY_BOOT_BUTTON) -#include "shared-bindings/digitalio/DigitalInOut.h" -#include "shared-bindings/time/__init__.h" -#endif #include "shared-bindings/microcontroller/Processor.h" #include "shared-bindings/microcontroller/ResetReason.h" #include "shared-module/storage/__init__.h" #include "common-hal/_bleio/__init__.h" +#include "supervisor/port.h" #include "supervisor/shared/serial.h" #include "supervisor/shared/status_leds.h" #include "supervisor/shared/tick.h" @@ -238,18 +235,10 @@ void supervisor_bluetooth_init(void) { new_status_color(BLACK); } #endif - // Init the boot button every time in case it is used for LEDs. - #ifdef CIRCUITPY_BOOT_BUTTON - digitalio_digitalinout_obj_t boot_button; - common_hal_digitalio_digitalinout_construct(&boot_button, CIRCUITPY_BOOT_BUTTON); - common_hal_digitalio_digitalinout_switch_to_input(&boot_button, PULL_UP); - common_hal_time_delay_ms(1); - bool button_pressed = !common_hal_digitalio_digitalinout_get_value(&boot_button); - common_hal_digitalio_digitalinout_deinit(&boot_button); - if (button_pressed) { + if (port_boot_button_pressed()) { boot_in_discovery_mode = true; + break; } - #endif diff = supervisor_ticks_ms64() - start_ticks; } if (boot_in_discovery_mode) { diff --git a/supervisor/shared/port.c b/supervisor/shared/port.c index 72bb45b8a270c..65e11f2fbf329 100644 --- a/supervisor/shared/port.c +++ b/supervisor/shared/port.c @@ -12,6 +12,11 @@ #include "lib/tlsf/tlsf.h" +#ifdef CIRCUITPY_BOOT_BUTTON +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/time/__init__.h" +#endif + static tlsf_t heap; MP_WEAK void port_wake_main_task(void) { @@ -60,3 +65,18 @@ MP_WEAK size_t port_heap_get_largest_free_size(void) { // IDF does this. Not sure why. return tlsf_fit_size(heap, max_size); } + +MP_WEAK bool port_boot_button_pressed(void) { + #if defined(CIRCUITPY_BOOT_BUTTON) + // Init/deinit the boot button every time in case it is used for LEDs. + digitalio_digitalinout_obj_t boot_button; + common_hal_digitalio_digitalinout_construct(&boot_button, CIRCUITPY_BOOT_BUTTON); + common_hal_digitalio_digitalinout_switch_to_input(&boot_button, PULL_UP); + common_hal_time_delay_ms(1); + bool button_pressed = !common_hal_digitalio_digitalinout_get_value(&boot_button); + common_hal_digitalio_digitalinout_deinit(&boot_button); + return button_pressed; + #else + return false; + #endif +} diff --git a/supervisor/shared/safe_mode.c b/supervisor/shared/safe_mode.c index 85c49451bc718..5f24618f7f39b 100644 --- a/supervisor/shared/safe_mode.c +++ b/supervisor/shared/safe_mode.c @@ -8,10 +8,6 @@ #include "mphalport.h" -#if defined(CIRCUITPY_BOOT_BUTTON) -#include "shared-bindings/digitalio/DigitalInOut.h" -#include "shared-bindings/time/__init__.h" -#endif #include "shared-bindings/microcontroller/Processor.h" #include "shared-bindings/microcontroller/ResetReason.h" @@ -78,19 +74,10 @@ safe_mode_t wait_for_safe_mode_reset(void) { new_status_color(BLACK); } #endif - // Init the boot button every time in case it is used for LEDs. - #ifdef CIRCUITPY_BOOT_BUTTON - digitalio_digitalinout_obj_t boot_button; - common_hal_digitalio_digitalinout_construct(&boot_button, CIRCUITPY_BOOT_BUTTON); - common_hal_digitalio_digitalinout_switch_to_input(&boot_button, PULL_UP); - common_hal_time_delay_ms(1); - bool button_pressed = !common_hal_digitalio_digitalinout_get_value(&boot_button); - common_hal_digitalio_digitalinout_deinit(&boot_button); - if (button_pressed) { + if (port_boot_button_pressed()) { boot_in_safe_mode = true; break; } - #endif diff = supervisor_ticks_ms64() - start_ticks; } #if CIRCUITPY_STATUS_LED @@ -142,7 +129,7 @@ void print_safe_mode_message(safe_mode_t reason) { case SAFE_MODE_USER: #if defined(BOARD_USER_SAFE_MODE_ACTION) message = BOARD_USER_SAFE_MODE_ACTION; - #elif defined(CIRCUITPY_BOOT_BUTTON) + #elif defined(CIRCUITPY_BOOT_BUTTON) || CIRCUITPY_BOOT_BUTTON_NO_GPIO message = MP_ERROR_TEXT("You pressed the BOOT button at start up"); #else message = MP_ERROR_TEXT("You pressed the reset button during boot.");