Skip to content

Commit

Permalink
refactor(gpio): improve internal structure for clarity
Browse files Browse the repository at this point in the history
Signed-off-by: Zhouqi Jiang <[email protected]>
  • Loading branch information
luojia65 committed Jan 6, 2025
1 parent 0b42c8e commit 2297d7e
Show file tree
Hide file tree
Showing 8 changed files with 568 additions and 506 deletions.
520 changes: 14 additions & 506 deletions allwinner-hal/src/gpio.rs

Large diffs are not rendered by default.

57 changes: 57 additions & 0 deletions allwinner-hal/src/gpio/disabled.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use super::{
eint::EintPad,
function::Function,
input::Input,
mode::{set_mode, HasMode},
output::Output,
register::RegisterBlock,
};

/// Disabled GPIO pad.
pub struct Disabled<'a, const P: char, const N: u8> {
gpio: &'a RegisterBlock,
}

impl<'a, const P: char, const N: u8> Disabled<'a, P, N> {
/// Configures the pad to operate as an input pad.
#[inline]
pub fn into_input(self) -> Input<'a, P, N> {
set_mode(self)
}
/// Configures the pad to operate as an output pad.
#[inline]
pub fn into_output(self) -> Output<'a, P, N> {
set_mode(self)
}
/// Configures the pad to operate as an alternate function pad.
#[inline]
pub fn into_function<const F: u8>(self) -> Function<'a, P, N, F> {
set_mode(self)
}
/// Configures the pad to operate as an external interrupt pad.
#[inline]
pub fn into_eint(self) -> EintPad<'a, P, N> {
set_mode(self)
}

/// Internal constructor for ROM runtime. Do not use.
#[doc(hidden)]
#[inline(always)]
pub const unsafe fn __new(gpio: &'a RegisterBlock) -> Self {
Self { gpio }
}
}

impl<'a, const P: char, const N: u8> HasMode<'a> for Disabled<'a, P, N> {
const P: char = P;
const N: u8 = N;
const VALUE: u8 = 15;
#[inline]
fn gpio(&self) -> &'a RegisterBlock {
self.gpio
}
#[inline]
unsafe fn from_gpio(gpio: &'a RegisterBlock) -> Self {
Self { gpio }
}
}
100 changes: 100 additions & 0 deletions allwinner-hal/src/gpio/eint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use super::{
disabled::Disabled,
function::Function,
input::Input,
mode::{set_mode, HasMode},
output::Output,
port_cfg_index, port_index,
register::RegisterBlock,
};

/// External interrupt mode pad.
pub struct EintPad<'a, const P: char, const N: u8> {
gpio: &'a RegisterBlock,
}

impl<'a, const P: char, const N: u8> EintPad<'a, P, N> {
/// Configures the pad to operate as an input pad.
#[inline]
pub fn into_input(self) -> Input<'a, P, N> {
set_mode(self)
}
/// Configures the pad to operate as an output pad.
#[inline]
pub fn into_output(self) -> Output<'a, P, N> {
set_mode(self)
}
/// Configures the pad to operate as an alternate function pad.
#[inline]
pub fn into_function<const F: u8>(self) -> Function<'a, P, N, F> {
set_mode(self)
}
/// Configures the pad to operate as a disabled pad.
#[inline]
pub fn into_disabled(self) -> Disabled<'a, P, N> {
set_mode(self)
}
}

impl<'a, const P: char, const N: u8> EintPad<'a, P, N> {
#[inline]
pub fn listen(&mut self, event: Event) {
let event_id = match event {
Event::PositiveEdge => 0,
Event::NegativeEdge => 1,
Event::HighLevel => 2,
Event::LowLevel => 3,
Event::BothEdges => 4,
};
let (port_idx, cfg_reg_idx, mask, cfg_field_idx) = const {
let (port_idx, cfg_reg_idx, cfg_field_idx) = port_cfg_index(P, N);
let mask = !(0xF << cfg_field_idx);
(port_idx, cfg_reg_idx, mask, cfg_field_idx)
};
let value = event_id << cfg_field_idx;
let cfg_reg = &self.gpio.eint[port_idx].cfg[cfg_reg_idx];
unsafe { cfg_reg.modify(|cfg| (cfg & mask) | value) };
}
#[inline]
pub fn enable_interrupt(&mut self) {
let idx = const { port_index(P) };
unsafe { self.gpio.eint[idx].ctl.modify(|value| value | (1 << N)) }
}
#[inline]
pub fn disable_interrupt(&mut self) {
let idx = const { port_index(P) };
unsafe { self.gpio.eint[idx].ctl.modify(|value| value & !(1 << N)) }
}
#[inline]
pub fn clear_interrupt_pending_bit(&mut self) {
unsafe { self.gpio.eint[const { port_index(P) }].status.write(1 << N) }
}
#[inline]
pub fn check_interrupt(&mut self) -> bool {
self.gpio.eint[const { port_index(P) }].status.read() & (1 << N) != 0
}
}

impl<'a, const P: char, const N: u8> HasMode<'a> for EintPad<'a, P, N> {
const P: char = P;
const N: u8 = N;
const VALUE: u8 = 14;
#[inline]
fn gpio(&self) -> &'a RegisterBlock {
self.gpio
}
#[inline]
unsafe fn from_gpio(gpio: &'a RegisterBlock) -> Self {
Self { gpio }
}
}

/// External interrupt event.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Event {
PositiveEdge,
NegativeEdge,
HighLevel,
LowLevel,
BothEdges,
}
73 changes: 73 additions & 0 deletions allwinner-hal/src/gpio/function.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use super::{
disabled::Disabled,
eint::EintPad,
input::Input,
mode::{borrow_with_mode, set_mode, HasMode},
output::Output,
register::RegisterBlock,
};

/// Alternate function pad.
///
/// F should be in 2..=8.
pub struct Function<'a, const P: char, const N: u8, const F: u8> {
gpio: &'a RegisterBlock,
}

impl<'a, const P: char, const N: u8, const F: u8> Function<'a, P, N, F> {
/// Configures the pad to operate as an input pad.
#[inline]
pub fn into_input(self) -> Input<'a, P, N> {
set_mode(self)
}
/// Configures the pad to operate as an output pad.
#[inline]
pub fn into_output(self) -> Output<'a, P, N> {
set_mode(self)
}
/// Configures the pad to operate as an alternate function pad.
#[inline]
pub fn into_function<const F2: u8>(self) -> Function<'a, P, N, F2> {
set_mode(self)
}
/// Configures the pad to operate as an external interrupt pad.
#[inline]
pub fn into_eint(self) -> EintPad<'a, P, N> {
set_mode(self)
}
/// Configures the pad to operate as a disabled pad.
#[inline]
pub fn into_disabled(self) -> Disabled<'a, P, N> {
set_mode(self)
}
/// Borrows the pad to temporarily use it as an input pad.
#[inline]
pub fn with_input<G, T>(&mut self, f: G) -> T
where
G: FnOnce(&mut Input<'a, P, N>) -> T,
{
borrow_with_mode(self, f)
}
/// Borrows the pad to temporarily use it as an output pad.
#[inline]
pub fn with_output<G, T>(&mut self, f: G) -> T
where
G: FnOnce(&mut Output<'a, P, N>) -> T,
{
borrow_with_mode(self, f)
}
}

impl<'a, const P: char, const N: u8, const F: u8> HasMode<'a> for Function<'a, P, N, F> {
const P: char = P;
const N: u8 = N;
const VALUE: u8 = F;
#[inline]
fn gpio(&self) -> &'a RegisterBlock {
self.gpio
}
#[inline]
unsafe fn from_gpio(gpio: &'a RegisterBlock) -> Self {
Self { gpio }
}
}
82 changes: 82 additions & 0 deletions allwinner-hal/src/gpio/input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use super::{
disabled::Disabled,
eint::EintPad,
function::Function,
mode::{borrow_with_mode, set_mode, HasMode},
output::Output,
port_index,
register::RegisterBlock,
};

/// Input mode pad.
pub struct Input<'a, const P: char, const N: u8> {
gpio: &'a RegisterBlock,
}

impl<'a, const P: char, const N: u8> Input<'a, P, N> {
/// Configures the pad to operate as an output pad.
#[inline]
pub fn into_output(self) -> Output<'a, P, N> {
set_mode(self)
}
/// Configures the pad to operate as an alternate function pad.
#[inline]
pub fn into_function<const F: u8>(self) -> Function<'a, P, N, F> {
set_mode(self)
}
/// Configures the pad to operate as an external interrupt pad.
#[inline]
pub fn into_eint(self) -> EintPad<'a, P, N> {
set_mode(self)
}
/// Configures the pad to operate as a disabled pad.
#[inline]
pub fn into_disabled(self) -> Disabled<'a, P, N> {
set_mode(self)
}
/// Borrows the pad to temporarily use it as an output pad.
#[inline]
pub fn with_output<F, T>(&mut self, f: F) -> T
where
F: FnOnce(&mut Output<'a, P, N>) -> T,
{
borrow_with_mode(self, f)
}
/// Borrows the pad to temporarily use it an alternate function pad.
#[inline]
pub fn with_function<const G: u8, F, T>(&mut self, f: F) -> T
where
F: FnOnce(&mut Function<'a, P, N, G>) -> T,
{
borrow_with_mode(self, f)
}
}

impl<'a, const P: char, const N: u8> embedded_hal::digital::ErrorType for Input<'a, P, N> {
type Error = core::convert::Infallible;
}

impl<'a, const P: char, const N: u8> embedded_hal::digital::InputPin for Input<'a, P, N> {
#[inline]
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(self.gpio.port[const { port_index(P) }].dat.read() & (1 << N) != 0)
}
#[inline]
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(self.gpio.port[const { port_index(P) }].dat.read() & (1 << N) == 0)
}
}

impl<'a, const P: char, const N: u8> HasMode<'a> for Input<'a, P, N> {
const P: char = P;
const N: u8 = N;
const VALUE: u8 = 0;
#[inline]
fn gpio(&self) -> &'a RegisterBlock {
self.gpio
}
#[inline]
unsafe fn from_gpio(gpio: &'a RegisterBlock) -> Self {
Self { gpio }
}
}
55 changes: 55 additions & 0 deletions allwinner-hal/src/gpio/mode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use super::{port_cfg_index, register::RegisterBlock};

/// Internal function to set GPIO pad mode.
#[inline]
pub fn set_mode<'a, T, U>(value: T) -> U
where
T: HasMode<'a>,
U: HasMode<'a>,
{
// take ownership of pad
let gpio = value.gpio();
unsafe { write_mode::<T, U>(gpio) };
// return ownership of pad
unsafe { U::from_gpio(gpio) }
}

#[inline]
pub fn borrow_with_mode<'a, T, U, F, R>(value: &mut T, f: F) -> R
where
T: HasMode<'a>,
U: HasMode<'a>,
F: FnOnce(&mut U) -> R,
{
// take ownership of pad
let gpio = value.gpio();
// set pad to new mode
unsafe { write_mode::<T, U>(gpio) };
let mut pad = unsafe { U::from_gpio(gpio) };
let val = f(&mut pad);
// restore pad to original mode
unsafe { write_mode::<T, T>(gpio) };
val
}

#[inline]
unsafe fn write_mode<'a, T: HasMode<'a>, U: HasMode<'a>>(gpio: &RegisterBlock) {
// calculate mask, value and register address
let (mask, value, port_idx, cfg_reg_idx) = const {
let (port_idx, cfg_reg_idx, cfg_field_idx) = port_cfg_index(T::P, T::N);
let mask = !(0xF << cfg_field_idx);
let value = (U::VALUE as u32) << cfg_field_idx;
(mask, value, port_idx, cfg_reg_idx)
};
// apply configuration
let cfg_reg = &gpio.port[port_idx].cfg[cfg_reg_idx];
unsafe { cfg_reg.modify(|cfg| (cfg & mask) | value) };
}

pub trait HasMode<'a> {
const P: char;
const N: u8;
const VALUE: u8;
fn gpio(&self) -> &'a RegisterBlock;
unsafe fn from_gpio(gpio: &'a RegisterBlock) -> Self;
}
Loading

0 comments on commit 2297d7e

Please sign in to comment.