aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Mansell <[email protected]>2023-09-23 17:34:47 +1200
committerDario Nieuwenhuis <[email protected]>2023-10-07 01:39:29 +0200
commit3e054a6f0d3ba018315f7cb7f0a373221e15737a (patch)
tree8446ca7d411de6cb18ba2e3bb581ae38857fdf44
parentc6d53e7bce9a9f04b2d479a150c4e1aee1bb4ea8 (diff)
rp2040: implement BOOTSEL button support
-rw-r--r--embassy-rp/src/bootsel.rs81
-rw-r--r--embassy-rp/src/lib.rs1
2 files changed, 82 insertions, 0 deletions
diff --git a/embassy-rp/src/bootsel.rs b/embassy-rp/src/bootsel.rs
new file mode 100644
index 000000000..69d620e8d
--- /dev/null
+++ b/embassy-rp/src/bootsel.rs
@@ -0,0 +1,81 @@
1//! Boot Select button
2//!
3//! The RP2040 rom supports a BOOTSEL button that is used to enter the USB bootloader
4//! if held during reset. To avoid wasting GPIO pins, the button is multiplexed onto
5//! the CS pin of the QSPI flash, but that makes it somewhat expensive and complicated
6//! to utilize outside of the rom's bootloader.
7//!
8//! This module provides functionality to poll BOOTSEL from an embassy application.
9
10use crate::flash::in_ram;
11
12/// Polls the BOOTSEL button. Returns true if the button is pressed.
13///
14/// Polling isn't cheap, as this function waits for core 1 to finish it's current
15/// task and for any DMAs from flash to complete
16pub fn poll_bootsel() -> bool {
17 let mut cs_status = Default::default();
18
19 unsafe { in_ram(|| cs_status = ram_helpers::read_cs_status()) }.expect("Must be called from Core 0");
20
21 // bootsel is active low, so invert
22 !cs_status.infrompad()
23}
24
25mod ram_helpers {
26 use rp_pac::io::regs::GpioStatus;
27
28 /// Temporally reconfigures the CS gpio and returns the GpioStatus.
29
30 /// This function runs from RAM so it can disable flash XIP.
31 ///
32 /// # Safety
33 ///
34 /// The caller must ensure flash is idle and will remain idle.
35 /// This function must live in ram. It uses inline asm to avoid any
36 /// potential calls to ABI functions that might be in flash.
37 #[inline(never)]
38 #[link_section = ".data.ram_func"]
39 #[cfg(target_arch = "arm")]
40 pub unsafe fn read_cs_status() -> GpioStatus {
41 let result: u32;
42
43 // Magic value, used as both OEOVER::DISABLE and delay loop counter
44 let magic = 0x2000;
45
46 core::arch::asm!(
47 ".equiv GPIO_STATUS, 0x0",
48 ".equiv GPIO_CTRL, 0x4",
49
50 "ldr {orig_ctrl}, [{cs_gpio}, $GPIO_CTRL]",
51
52 // The BOOTSEL pulls the flash's CS line low though a 1K resistor.
53 // this is weak enough to avoid disrupting normal operation.
54 // But, if we disable CS's output drive and allow it to float...
55 "str {val}, [{cs_gpio}, $GPIO_CTRL]",
56
57 // ...then wait for the state to settle...
58 "1:", // ~4000 cycle delay loop
59 "subs {val}, #8",
60 "bne 1b",
61
62 // ...we can read the current state of bootsel
63 "ldr {val}, [{cs_gpio}, $GPIO_STATUS]",
64
65 // Finally, restore CS to normal operation so XIP can continue
66 "str {orig_ctrl}, [{cs_gpio}, $GPIO_CTRL]",
67
68 cs_gpio = in(reg) rp_pac::IO_QSPI.gpio(1).as_ptr(),
69 orig_ctrl = out(reg) _,
70 val = inout(reg) magic => result,
71 options(nostack),
72 );
73
74 core::mem::transmute(result)
75 }
76
77 #[cfg(not(target_arch = "arm"))]
78 pub unsafe fn read_cs_status() -> GpioStatus {
79 unimplemented!()
80 }
81}
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index e8f818bcf..fb9189203 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -10,6 +10,7 @@ mod critical_section_impl;
10mod intrinsics; 10mod intrinsics;
11 11
12pub mod adc; 12pub mod adc;
13pub mod bootsel;
13pub mod clocks; 14pub mod clocks;
14pub mod dma; 15pub mod dma;
15pub mod flash; 16pub mod flash;