use crate::Instance; use core::num::NonZero; use core::pin::Pin; use fusible::event_flags::{EventFlagsContext, GetError, GetOption, SetOption}; use crate::ral::gpio; use fusible::{event_flags::EventFlags, interrupt_control}; pub struct GpioContext { flags: EventFlagsContext<'static>, } unsafe impl Sync for GpioPort {} impl GpioContext { /// # Safety /// /// You must call this in the interrupt associated with this GPIO context. /// You must have already created the context. #[inline(always)] pub unsafe fn on_interrupt(&'static self, port: Instance) { // Safety: caller claims that we've already created the context. // We are the "owners" of the GPIO port we're pointing at, and // we control interrupts when accessing the IMR and ISR registers. unsafe { let imr = crate::read_reg!(gpio, port, IMR); let isr = crate::read_reg!(gpio, port, ISR); crate::write_reg!(gpio, port, IMR, imr & !isr); crate::write_reg!(gpio, port, ISR, isr); Pin::static_ref(&self.flags) .assume_created() .set(isr, SetOption::Or) } } /// # Panics /// /// Panics if you've already created the context. Also panics if /// called in an interrupt context, or if called before the kernel /// is entered. pub unsafe fn create(&'static self, port: Instance) -> GpioPort { let flags = EventFlags::create(Pin::static_ref(&self.flags), &Default::default()).unwrap(); GpioPort { flags, port } } pub const fn new() -> Self { Self { flags: EventFlags::context(), } } } #[derive(Clone)] pub struct GpioPort { port: Instance, flags: &'static EventFlags, } unsafe impl Send for GpioPort {} impl GpioPort { pub fn configure_multiple_outputs(&self, mask: Mask) { interrupt_control::with_disabled(|| { crate::modify_reg!(gpio, self.port, GDIR, |gdir| gdir | mask.get()) }) } pub fn configure_multiple_inputs(&self, mask: Mask) { interrupt_control::with_disabled(|| { crate::modify_reg!(gpio, self.port, GDIR, |gdir| gdir & !mask.get()) }); } #[inline] pub fn set_multiple(&self, mask: Mask) { crate::write_reg!(gpio, self.port, DR_SET, mask.get()); } #[inline] pub fn clear_multiple(&self, mask: Mask) { crate::write_reg!(gpio, self.port, DR_CLEAR, mask.get()); } #[inline] pub fn toggle_multiple(&self, mask: Mask) { crate::write_reg!(gpio, self.port, DR_TOGGLE, mask.get()); } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] pub enum Interrupt { LowLevel = 0, HighLevel = 1, RisingEdge = 2, FallingEdge = 3, EitherEdge = 4, } impl GpioPort { pub fn configure_interrupt(&self, pin: IO, interrupt: Interrupt) { let pin = pin as u32; let clear_edge_sel = !(1 << pin); let set_edge_sel = ((interrupt == Interrupt::EitherEdge) as u32) << pin; let icr_offset = (pin % 16) * 2; let set_icr = (interrupt as u32 & 0b11) << icr_offset; let clear_icr = !(0b11 << icr_offset); let icr1 = pin < 16; interrupt_control::with_disabled(|| { let mut icr = if icr1 { crate::read_reg!(gpio, self.port, ICR1) } else { crate::read_reg!(gpio, self.port, ICR2) }; icr &= clear_icr; icr |= set_icr; if icr1 { crate::write_reg!(gpio, self.port, ICR1, icr); } else { crate::write_reg!(gpio, self.port, ICR2, icr); } let mut edge_sel = crate::read_reg!(gpio, self.port, EDGE_SEL); edge_sel &= clear_edge_sel; edge_sel |= set_edge_sel; crate::write_reg!(gpio, self.port, EDGE_SEL, edge_sel); }); } pub fn block_on_interrupts(&self, mask: Mask) -> Option { interrupt_control::with_disabled(|| { crate::modify_reg!(gpio, self.port, IMR, |imr| imr | mask.get()); }); match self.flags.get(mask, GetOption::OrClear) { Ok(mask) => Some(mask), Err(GetError::WaitAborted) => None, Err(GetError::InvalidWait) => panic!(), } } } impl GpioPort { pub fn make_output(&self, io: IO) -> GpioOutput { self.configure_multiple_outputs(io.mask()); GpioOutput { port: self.clone(), io, } } pub fn make_input(&self, io: IO) -> GpioInput { self.configure_multiple_inputs(io.mask()); GpioInput { port: self.clone(), io, } } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u32)] pub enum IO { IO00, IO01, IO02, IO03, IO04, IO05, IO06, IO07, IO08, IO09, IO10, IO11, IO12, IO13, IO14, IO15, IO16, IO17, IO18, IO19, IO20, IO21, IO22, IO23, IO24, IO25, IO26, IO27, IO28, IO29, IO30, IO31, } impl IO { #[inline] #[must_use] pub const fn mask(self) -> Mask { // Safety: the value is at least 1. unsafe { NonZero::new_unchecked(1 << self.offset()) } } #[inline] #[must_use] pub const fn offset(self) -> u32 { self as u32 } } pub type Mask = NonZero; pub struct GpioOutput { port: GpioPort, io: IO, } impl GpioOutput { #[inline] pub fn set(&self) { self.port.set_multiple(self.io.mask()); } #[inline] pub fn clear(&self) { self.port.clear_multiple(self.io.mask()); } #[inline] pub fn toggle(&self) { self.port.toggle_multiple(self.io.mask()); } } impl super::ToggleOutput for GpioOutput { fn toggle(&self) { GpioOutput::toggle(&self); } } pub struct GpioInput { port: GpioPort, io: IO, } impl GpioInput { pub fn configure_interrupt(&self, interrupt: Interrupt) { self.port.configure_interrupt(self.io, interrupt); } pub fn block_on_interrupt(&self) { self.port.block_on_interrupts(self.io.mask()); } } impl super::BlockingInput for GpioInput { fn block_on_interrupt(&self) { GpioInput::block_on_interrupt(&self); } }