rp2040: implement BOOTSEL button support
This commit is contained in:
parent
c6d53e7bce
commit
3e054a6f0d
81
embassy-rp/src/bootsel.rs
Normal file
81
embassy-rp/src/bootsel.rs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
//! 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;
|
||||||
|
|
||||||
|
/// 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 poll_bootsel() -> 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!()
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@ mod critical_section_impl;
|
|||||||
mod intrinsics;
|
mod intrinsics;
|
||||||
|
|
||||||
pub mod adc;
|
pub mod adc;
|
||||||
|
pub mod bootsel;
|
||||||
pub mod clocks;
|
pub mod clocks;
|
||||||
pub mod dma;
|
pub mod dma;
|
||||||
pub mod flash;
|
pub mod flash;
|
||||||
|
Loading…
Reference in New Issue
Block a user