Skip to content

Commit

Permalink
Add reference counting to GPIO pins to init/deinit clock control to p…
Browse files Browse the repository at this point in the history
…orts
  • Loading branch information
tullom committed Oct 23, 2024
1 parent dc04276 commit ebbf70c
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 7 deletions.
18 changes: 12 additions & 6 deletions examples/rt685s-evk/src/bin/gpio-blinky.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ extern crate embassy_imxrt_examples;
use defmt::info;
use embassy_executor::Spawner;
use embassy_imxrt::gpio;
use embassy_time::Timer;
use embassy_imxrt::pac;
// use embassy_time::Timer;

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
Expand All @@ -23,9 +24,14 @@ async fn main(_spawner: Spawner) {
gpio::SlewRate::Standard,
);

loop {
info!("Toggling LED");
led.toggle();
Timer::after_millis(1000).await;
}
let cc1 = unsafe { pac::Clkctl1::steal() };

assert!(cc1.pscctl1().read().hsgpio0_clk().is_enable_clock());
info!("Toggling LED");
led.toggle();
assert!(cc1.pscctl1().read().hsgpio0_clk().is_enable_clock());

drop(led);

assert!(cc1.pscctl1().read().hsgpio0_clk().is_disable_clock());
}
67 changes: 66 additions & 1 deletion src/iopctl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@
//!
//! Also known as IO Pin Configuration (IOCON)
use crate::pac::{iopctl, Iopctl};
use core::sync::atomic::AtomicU32;

use core::sync::atomic::Ordering::SeqCst;

use crate::{
clocks::disable,
pac::{iopctl, Iopctl},
peripherals::{HSGPIO0, HSGPIO1, HSGPIO2, HSGPIO3, HSGPIO4, HSGPIO5, HSGPIO6, HSGPIO7},
};

// A generic pin of any type.
//
Expand Down Expand Up @@ -176,6 +184,30 @@ pub trait IopctlPin: SealedPin {
fn reset(&self) -> &Self;
}

struct PortUsecountWrapper {
usecount: AtomicU32,
}

impl PortUsecountWrapper {
const fn new() -> Self {
Self {
usecount: AtomicU32::new(0),
}
}
}
const PORT_COUNT: usize = 8;

static mut PORT_USECOUNT: [PortUsecountWrapper; PORT_COUNT] = [
PortUsecountWrapper::new(),
PortUsecountWrapper::new(),
PortUsecountWrapper::new(),
PortUsecountWrapper::new(),
PortUsecountWrapper::new(),
PortUsecountWrapper::new(),
PortUsecountWrapper::new(),
PortUsecountWrapper::new(),
];

/// Represents a pin peripheral created at run-time from given port and pin numbers.
pub struct AnyPin {
pin_port: u8,
Expand Down Expand Up @@ -209,6 +241,13 @@ impl AnyPin {

// SAFETY: This is safe assuming the caller of this function satisfies the safety requirements above.
let reg = unsafe { &*(Iopctl::ptr().byte_offset(offset as isize) as *const _) };

// SAFETY: The array access is safe because we control the pin implementation in impl_pin!
// and we are using an atomic variable to increment.
unsafe {
PORT_USECOUNT[port as usize].usecount.fetch_add(1, SeqCst);
}

Self {
pin_port: port * 32 + pin,
reg,
Expand All @@ -221,6 +260,32 @@ impl AnyPin {
}
}

impl Drop for AnyPin {
fn drop(&mut self) {
// Extract port number from offset
let port = self.pin_port / 32;

// Safe since we are using an atomic fetch and subtract
unsafe {
// Disable clocks GPIO Port if this is the last pin using this port
if PORT_USECOUNT[port as usize].usecount.fetch_sub(1, SeqCst) == 1 {
// Does this need a mutex?
match port {
0 => disable::<HSGPIO0>(),
1 => disable::<HSGPIO1>(),
2 => disable::<HSGPIO2>(),
3 => disable::<HSGPIO3>(),
4 => disable::<HSGPIO4>(),
5 => disable::<HSGPIO5>(),
6 => disable::<HSGPIO6>(),
7 => disable::<HSGPIO7>(),
_ => panic!(),
}
}
}
}
}

// This allows AnyPin to be used in HAL constructors that require types
// which impl Peripheral. Used primarily by GPIO HAL to convert type-erased
// GPIO pins back into an Output or Input pin specifically.
Expand Down

0 comments on commit ebbf70c

Please sign in to comment.