diff options
Diffstat (limited to 'crates/imxrt/src/drivers/gpio.rs')
| -rw-r--r-- | crates/imxrt/src/drivers/gpio.rs | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/crates/imxrt/src/drivers/gpio.rs b/crates/imxrt/src/drivers/gpio.rs new file mode 100644 index 0000000..b1f883b --- /dev/null +++ b/crates/imxrt/src/drivers/gpio.rs @@ -0,0 +1,265 @@ +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<gpio::RegisterBlock>) { + // 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<gpio::RegisterBlock>) -> 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<gpio::RegisterBlock>, + 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<Mask> { + 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<u32>; + +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); + } +} |
