Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add reference counting to GPIO pins to init/deinit clock control to port #139

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion examples/rt685s-evk/src/bin/gpio-blinky.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ extern crate embassy_imxrt_examples;

use defmt::info;
use embassy_executor::Spawner;
use embassy_imxrt::gpio;
use embassy_imxrt::{gpio, pac};
use embassy_time::Timer;

#[embassy_executor::main]
Expand All @@ -14,6 +14,13 @@ async fn main(_spawner: Spawner) {

info!("Initializing GPIO");

let cc1 = unsafe { pac::Clkctl1::steal() };

assert!(
cc1.pscctl1().read().hsgpio0_clk().is_disable_clock(),
"GPIO port 0 clock was enabled before any GPIO pins were created!"
);

let mut led = gpio::Output::new(
p.PIO0_26,
gpio::Level::Low,
Expand All @@ -22,6 +29,11 @@ async fn main(_spawner: Spawner) {
gpio::SlewRate::Standard,
);

assert!(
cc1.pscctl1().read().hsgpio0_clk().is_enable_clock(),
"GPIO port 0 clock is still disabled even after a GPIO pin is created!"
);

loop {
info!("Toggling LED");
led.toggle();
Expand Down
31 changes: 27 additions & 4 deletions examples/rt685s-evk/src/bin/gpio-flex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ extern crate embassy_imxrt_examples;

use defmt::{assert, info};
use embassy_executor::Spawner;
use embassy_imxrt::gpio;
use embassy_imxrt::gpio::SenseDisabled;
use embassy_imxrt::{gpio, pac};
use embassy_time::Timer;

#[embassy_executor::main]
Expand All @@ -15,9 +15,14 @@ async fn main(_spawner: Spawner) {

info!("Initializing GPIO");

let cc1 = unsafe { pac::Clkctl1::steal() };
assert!(cc1.pscctl1().read().hsgpio1_clk().is_disable_clock());

// Start with a level sensing disabled, output only state
let flex = gpio::Flex::<SenseDisabled>::new(p.PIO1_0);

assert!(cc1.pscctl1().read().hsgpio1_clk().is_enable_clock());

// enable level sensing
let mut flex = flex.enable_sensing();

Expand Down Expand Up @@ -67,9 +72,21 @@ async fn main(_spawner: Spawner) {

// set pin level high
flex.set_high();
flex.set_as_output(
gpio::DriveMode::PushPull,
gpio::DriveStrength::Normal,
gpio::SlewRate::Standard,
);

// re-enable level sensing
let mut flex = flex.enable_sensing();
// set pin level high
flex.set_high();
flex.set_as_output(
gpio::DriveMode::PushPull,
gpio::DriveStrength::Normal,
gpio::SlewRate::Standard,
);

// check pin level is high
assert!(flex.is_high());
Expand All @@ -80,16 +97,22 @@ async fn main(_spawner: Spawner) {
// check pin level is low
assert!(flex.is_low());

let mut flex = flex.disable_sensing();

// toggle pin
flex.toggle();

let flex = flex.enable_sensing();
flex.set_as_output(
gpio::DriveMode::PushPull,
gpio::DriveStrength::Normal,
gpio::SlewRate::Standard,
);

// check pin level is high
assert!(flex.is_high());

assert!(cc1.pscctl1().read().hsgpio1_clk().is_enable_clock());
drop(flex);
assert!(cc1.pscctl1().read().hsgpio1_clk().is_disable_clock());

loop {
Timer::after_millis(1000).await;
}
Expand Down
82 changes: 69 additions & 13 deletions src/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ use core::convert::Infallible;
use core::future::Future;
use core::marker::PhantomData;
use core::pin::Pin as FuturePin;
use core::sync::atomic::AtomicU32;
use core::sync::atomic::Ordering::Relaxed;
use core::task::{Context, Poll};

use embassy_hal_internal::interrupt::InterruptExt;
use embassy_sync::waitqueue::AtomicWaker;
use sealed::Sealed;

use crate::clocks::enable_and_reset;
use crate::clocks::{disable, enable_and_reset};
use crate::iopctl::IopctlPin;
pub use crate::iopctl::{AnyPin, DriveMode, DriveStrength, Function, Inverter, Pull, SlewRate};
use crate::{interrupt, into_ref, peripherals, Peripheral, PeripheralRef};
Expand Down Expand Up @@ -106,18 +108,9 @@ fn irq_handler(port_wakers: &[Option<&PortWaker>]) {
}

/// Initialization Logic
/// Note: GPIO port clocks are initialized in the clocks module.
/// Note: A GPIO port's clock is initialized whenever the first pin from a port is constructed
/// and deinitialized when the last pin from a port is dropped
pub(crate) fn init() {
// Enable GPIO clocks
enable_and_reset::<peripherals::HSGPIO0>();
enable_and_reset::<peripherals::HSGPIO1>();
enable_and_reset::<peripherals::HSGPIO2>();
enable_and_reset::<peripherals::HSGPIO3>();
enable_and_reset::<peripherals::HSGPIO4>();
enable_and_reset::<peripherals::HSGPIO5>();
enable_and_reset::<peripherals::HSGPIO6>();
enable_and_reset::<peripherals::HSGPIO7>();

// Enable INTA
interrupt::GPIO_INTA.unpend();

Expand Down Expand Up @@ -154,6 +147,17 @@ pub struct SenseDisabled;
impl Sealed for SenseDisabled {}
impl Sense for SenseDisabled {}

static PORT_USECOUNT: [AtomicU32; PORT_COUNT] = [
AtomicU32::new(0),
AtomicU32::new(0),
AtomicU32::new(0),
AtomicU32::new(0),
AtomicU32::new(0),
AtomicU32::new(0),
AtomicU32::new(0),
AtomicU32::new(0),
];

/// Flex pin.
///
/// This pin can be either an input or output pin. The output level register bit will
Expand Down Expand Up @@ -234,7 +238,7 @@ impl<S: Sense> Flex<'_, S> {
impl<S: Sense> Drop for Flex<'_, S> {
fn drop(&mut self) {
self.pin.reset();
// TODO: Disable clock for pin's port (assuming ref counted)?
decrement_port_usecount(self.pin.port());
}
}

Expand All @@ -247,6 +251,8 @@ impl<'d> Flex<'d, SenseEnabled> {
.disable_analog_multiplex()
.enable_input_buffer();

increment_port_usecount(pin.port());

Self {
pin: pin.map_into(),
_sense_mode: PhantomData::<SenseEnabled>,
Expand Down Expand Up @@ -327,6 +333,9 @@ impl<'d> Flex<'d, SenseEnabled> {
/// Return a new Flex pin instance with level sensing disabled.
///
/// Consumes less power than a flex pin with sensing enabled.
///
/// NOTE: A brand new flex pin is returned without any sense of previous state.
/// Be sure to set the pin level and pin direction (input or output)
#[must_use]
pub fn disable_sensing(self) -> Flex<'d, SenseDisabled> {
// Cloning the pin is ok since we consume self immediately
Expand All @@ -345,13 +354,18 @@ impl<'d> Flex<'d, SenseDisabled> {
.disable_analog_multiplex()
.disable_input_buffer();

increment_port_usecount(pin.port());

Self {
pin: pin.map_into(),
_sense_mode: PhantomData::<SenseDisabled>,
}
}

/// Return a new Flex pin instance with level sensing enabled.
///
/// NOTE: A brand new flex pin is returned without any sense of previous state.
/// Be sure to set the pin level and pin direction (input or output)
#[must_use]
pub fn enable_sensing(self) -> Flex<'d, SenseEnabled> {
// Cloning the pin is ok since we consume self immediately
Expand All @@ -361,6 +375,48 @@ impl<'d> Flex<'d, SenseDisabled> {
}
}

/// Increment port usecount counter
///
/// If the calling pin is the first pin in that port, then this enables the port's clock
fn increment_port_usecount(port: usize) {
let port_usecount = &PORT_USECOUNT[port];
let usecount = port_usecount.fetch_add(1, Relaxed);
if usecount == 0 {
match port {
0 => enable_and_reset::<peripherals::HSGPIO0>(),
1 => enable_and_reset::<peripherals::HSGPIO1>(),
2 => enable_and_reset::<peripherals::HSGPIO2>(),
3 => enable_and_reset::<peripherals::HSGPIO3>(),
4 => enable_and_reset::<peripherals::HSGPIO4>(),
5 => enable_and_reset::<peripherals::HSGPIO5>(),
6 => enable_and_reset::<peripherals::HSGPIO6>(),
7 => enable_and_reset::<peripherals::HSGPIO7>(),
_ => (),
}
}
}

/// Decrement port usecount counter
///
/// If the calling pin is the last pin in that port, then this disables the port's clock
fn decrement_port_usecount(port: usize) {
let port_usecount = &PORT_USECOUNT[port];
let usecount = port_usecount.fetch_sub(1, Relaxed);
if usecount == 1 {
match port {
0 => disable::<peripherals::HSGPIO0>(),
1 => disable::<peripherals::HSGPIO1>(),
2 => disable::<peripherals::HSGPIO2>(),
3 => disable::<peripherals::HSGPIO3>(),
4 => disable::<peripherals::HSGPIO4>(),
5 => disable::<peripherals::HSGPIO5>(),
6 => disable::<peripherals::HSGPIO6>(),
7 => disable::<peripherals::HSGPIO7>(),
_ => (),
}
}
}

/// Input pin
pub struct Input<'d> {
pin: Flex<'d, SenseEnabled>,
Expand Down
Loading