aboutsummaryrefslogtreecommitdiff
path: root/crates/imxrt/src/drivers/gpio.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/imxrt/src/drivers/gpio.rs')
-rw-r--r--crates/imxrt/src/drivers/gpio.rs265
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);
+ }
+}