@@ -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
112112type 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
115126struct 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