//! Boot Select button //! //! The RP2040 rom supports a BOOTSEL button that is used to enter the USB bootloader //! if held during reset. To avoid wasting GPIO pins, the button is multiplexed onto //! the CS pin of the QSPI flash, but that makes it somewhat expensive and complicated //! to utilize outside of the rom's bootloader. //! //! This module provides functionality to poll BOOTSEL from an embassy application. use crate::flash::in_ram; impl crate::peripherals::BOOTSEL { /// Polls the BOOTSEL button. Returns true if the button is pressed. /// /// Polling isn't cheap, as this function waits for core 1 to finish it's current /// task and for any DMAs from flash to complete pub fn is_pressed(&mut self) -> bool { let mut cs_status = Default::default(); unsafe { in_ram(|| cs_status = ram_helpers::read_cs_status()) }.expect("Must be called from Core 0"); // bootsel is active low, so invert !cs_status.infrompad() } } mod ram_helpers { use rp_pac::io::regs::GpioStatus; /// Temporally reconfigures the CS gpio and returns the GpioStatus. /// This function runs from RAM so it can disable flash XIP. /// /// # Safety /// /// The caller must ensure flash is idle and will remain idle. /// This function must live in ram. It uses inline asm to avoid any /// potential calls to ABI functions that might be in flash. #[inline(never)] #[link_section = ".data.ram_func"] #[cfg(target_arch = "arm")] pub unsafe fn read_cs_status() -> GpioStatus { let result: u32; // Magic value, used as both OEOVER::DISABLE and delay loop counter let magic = 0x2000; core::arch::asm!( ".equiv GPIO_STATUS, 0x0", ".equiv GPIO_CTRL, 0x4", "ldr {orig_ctrl}, [{cs_gpio}, $GPIO_CTRL]", // The BOOTSEL pulls the flash's CS line low though a 1K resistor. // this is weak enough to avoid disrupting normal operation. // But, if we disable CS's output drive and allow it to float... "str {val}, [{cs_gpio}, $GPIO_CTRL]", // ...then wait for the state to settle... "1:", // ~4000 cycle delay loop "subs {val}, #8", "bne 1b", // ...we can read the current state of bootsel "ldr {val}, [{cs_gpio}, $GPIO_STATUS]", // Finally, restore CS to normal operation so XIP can continue "str {orig_ctrl}, [{cs_gpio}, $GPIO_CTRL]", cs_gpio = in(reg) rp_pac::IO_QSPI.gpio(1).as_ptr(), orig_ctrl = out(reg) _, val = inout(reg) magic => result, options(nostack), ); core::mem::transmute(result) } #[cfg(not(target_arch = "arm"))] pub unsafe fn read_cs_status() -> GpioStatus { unimplemented!() } }