Skip to content

Commit 49b2ae3

Browse files
Detect SD Card insertion and removal.
1 parent 5d395d1 commit 49b2ae3

1 file changed

Lines changed: 115 additions & 9 deletions

File tree

src/main.rs

Lines changed: 115 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ use core::{
5858
cell::RefCell,
5959
convert::TryFrom,
6060
fmt::Write,
61-
sync::atomic::{AtomicBool, Ordering},
61+
sync::atomic::{AtomicBool, AtomicU32, Ordering},
6262
};
6363

6464
// Third Party Stuff
@@ -111,6 +111,17 @@ type I2cPins = (
111111

112112
type SpiBus = hal::Spi<hal::spi::Enabled, pac::SPI0, 8>;
113113

114+
/// What state our SD Card can be in
115+
#[derive(defmt::Format, Debug, Copy, Clone, PartialEq, Eq)]
116+
enum CardState {
117+
Unplugged,
118+
Uninitialised,
119+
#[allow(unused)]
120+
Online,
121+
#[allow(unused)]
122+
Errored,
123+
}
124+
114125
/// All the hardware we use on the Pico
115126
struct Hardware {
116127
/// All the pins we use on the Raspberry Pi Pico
@@ -142,6 +153,8 @@ struct Hardware {
142153
bootup_at: Duration,
143154
/// A Timer
144155
timer: hal::timer::Timer,
156+
/// the state of our SD Card
157+
card_state: CardState,
145158
}
146159

147160
/// Flips between true and false so we always send a unique read request
@@ -421,6 +434,9 @@ fn main() -> ! {
421434
// Did we start with an interrupt pending?
422435
INTERRUPT_PENDING.store(nirq_io.is_low().unwrap(), Ordering::Relaxed);
423436

437+
// Check for interrupts on start-up (particularly the SD card being in)
438+
hw.io_poll_interrupts(true);
439+
424440
critical_section::with(|cs| {
425441
HARDWARE.borrow(cs).replace(Some(hw));
426442
IRQ_PIN.borrow(cs).replace(Some(nirq_io))
@@ -694,6 +710,7 @@ impl Hardware {
694710
rtc: external_rtc,
695711
bootup_at: Duration::from_ticks(ticks_at_boot_us as u64),
696712
timer,
713+
card_state: CardState::Unplugged,
697714
},
698715
hal_pins.gpio20.into_pull_up_input(),
699716
)
@@ -857,7 +874,11 @@ impl Hardware {
857874
self.io_chip_write(mcp23s17::Register::GPPUB, 0xFF);
858875

859876
// Set up interrupts. We want the line to go low if anything on GPIOB is
860-
// low.
877+
// low (at least initially - we may flip the sense on the SD Card IRQ if
878+
// we find a card is inserted).
879+
880+
// Set IPOL so all pins active low
881+
self.io_chip_write(mcp23s17::Register::IPOLB, 0xFF);
861882

862883
// The INTCON register controls how the associated pin value is compared
863884
// for the interrupt-on-change feature. All the bits are set, so the
@@ -873,20 +894,94 @@ impl Hardware {
873894
// The default comparison value is configured in the DEFVAL register. If
874895
// enabled (via GPINTEN and INTCON) to compare against the DEFVAL
875896
// register, an opposite value on the associated pin will cause an
876-
// interrupt to occur.
897+
// interrupt to occur. This the logical value after the polarity switch
898+
// in `IPOLB` has been applied.
877899
self.io_chip_write(mcp23s17::Register::DEFVALB, 0x00);
878900
}
879901

880902
/// If the interrupt flag was set by the IRQ handler, read the interrupt
881903
/// bits from the MCP23S17.
882-
fn io_poll_interrupts(&mut self) {
883-
if INTERRUPT_PENDING.load(Ordering::Relaxed) {
884-
// The IO chip reports 0 for pending, 1 for not pending.
885-
self.interrupts_pending = self.io_read_interrupts() ^ 0xFF;
886-
defmt::debug!("MCP23S17 IRQ pins: 0b{:08b}", self.interrupts_pending);
904+
fn io_poll_interrupts(&mut self, force: bool) {
905+
if INTERRUPT_PENDING.load(Ordering::Relaxed) || force {
906+
// We use IOPOL to ensure a `1` bit means `Interrupt Pending`
907+
self.interrupts_pending = self.io_read_interrupts();
908+
let pol = self.io_chip_read(mcp23s17::Register::IPOLB);
909+
defmt::debug!(
910+
"MCP23S17 IRQ pins: 0b{:08b} (real 0b{:08b})",
911+
self.interrupts_pending,
912+
pol ^ self.interrupts_pending
913+
);
887914
// Change the debug LEDs so we can see the interrupts
888915
self.irq_count = self.irq_count.wrapping_add(1);
889916
self.set_debug_leds((self.irq_count & 0x0F) as u8);
917+
918+
for irq in 0..8 {
919+
let irq_bit = 1 << irq;
920+
if (self.interrupts_pending & irq_bit) != 0 {
921+
self.io_handle_irq(irq);
922+
}
923+
}
924+
925+
// We sometimes get stuck in a loop, due to bad programming. The IO
926+
// chip tells us we have an interrupt, but when we ask it which one,
927+
// it appears to say actually, there's no interrupt. This puts us
928+
// into an infinite loop, because the INTERRUPT_PENDING value isn't
929+
// cleared. This logic tries to catch that loop before sixty
930+
// bazillion lines of defmt are printed, causing the actual error to
931+
// disappear out of the terminal buffer.
932+
static BAD_IRQ_COUNT: AtomicU32 = AtomicU32::new(0);
933+
if self.interrupts_pending == 0 && !force {
934+
let count = BAD_IRQ_COUNT.load(Ordering::Relaxed) + 1;
935+
BAD_IRQ_COUNT.store(count, Ordering::Relaxed);
936+
if count > 5 {
937+
panic!("Unexpected interrupts in bagging area.");
938+
}
939+
} else {
940+
BAD_IRQ_COUNT.store(0, Ordering::Relaxed);
941+
}
942+
}
943+
}
944+
945+
/// Handle an IRQ on a slot
946+
fn io_handle_irq(&mut self, slot: u8) {
947+
match slot {
948+
1 => {
949+
// Slot 1 interrupt, which is the SD Card. Flip the sense, so we
950+
// get another interrupt when the card next changes state.
951+
let was_inserted = self.io_flip_input_level(1);
952+
defmt::info!(
953+
"SD Card state change: Card {}",
954+
if was_inserted { "inserted" } else { "removed" }
955+
);
956+
957+
self.card_state = match (self.card_state, was_inserted) {
958+
// Eject events
959+
(CardState::Unplugged, false) => {
960+
defmt::warn!("Spurious unplug event");
961+
CardState::Unplugged
962+
}
963+
(CardState::Errored | CardState::Uninitialised, false) => {
964+
defmt::info!("SD Card removed");
965+
CardState::Unplugged
966+
}
967+
(CardState::Online, false) => {
968+
defmt::warn!("Active SD Card removed!");
969+
CardState::Unplugged
970+
}
971+
// Insert events
972+
(CardState::Unplugged, true) => {
973+
defmt::info!("SD Card inserted, pending activation");
974+
CardState::Uninitialised
975+
}
976+
(_, true) => {
977+
defmt::warn!("Unexpected card insertion!?");
978+
CardState::Uninitialised
979+
}
980+
}
981+
}
982+
_ => {
983+
defmt::debug!("IRQ on slot {}", slot);
984+
}
890985
}
891986
}
892987

@@ -895,6 +990,17 @@ impl Hardware {
895990
fn io_read_interrupts(&mut self) -> u8 {
896991
self.io_chip_read(mcp23s17::Register::GPIOB)
897992
}
993+
994+
/// Flip which level is expected on an incoming interrupt pin.
995+
///
996+
/// Returns the old level
997+
fn io_flip_input_level(&mut self, slot: u8) -> bool {
998+
let mut mask = self.io_chip_read(mcp23s17::Register::IPOLB);
999+
let slot_mask = 1 << slot;
1000+
let current_level = (mask & slot_mask) != 0;
1001+
mask ^= slot_mask;
1002+
self.io_chip_write(mcp23s17::Register::IPOLB, mask);
1003+
current_level
8981004
}
8991005

9001006
/// Send a request to the BMC (a register read or a register write) and get
@@ -1050,7 +1156,7 @@ impl Hardware {
10501156
/// We ask for 8 bytes of data. We get `1` byte of 'length', then `N` bytes of valid data, and `32 - (N + 1)` bytes of padding.
10511157
fn bmc_read_ps2_keyboard_fifo(&mut self, out_buffer: &mut [u8; 8]) -> Result<usize, ()> {
10521158
// Now is a good time to poll for interrupts.
1053-
self.io_poll_interrupts();
1159+
self.io_poll_interrupts(false);
10541160
if !self.is_irq_pending_on_slot(0) {
10551161
// No point asking, the interrupt isn't set.
10561162
return Ok(0);

0 commit comments

Comments
 (0)