Files
embassy/embassy-imxrt/src/iopctl.rs
Felipe Balbi aa9a16e569 Add embassy-imxrt
Adds initial support for MIMXRT600 series MCUs from NXP. Subsequent
PRs will add more drivers.
2025-04-09 09:37:45 -07:00

718 lines
22 KiB
Rust

//! IO Pad Controller (IOPCTL)
//!
//! Also known as IO Pin Configuration (IOCON)
use crate::pac::{iopctl, Iopctl};
// A generic pin of any type.
//
// The actual pin type used here is arbitrary,
// as all PioM_N types provide the same methods.
//
// Merely need some pin type to cast a raw pointer
// to in order to access the provided methods.
#[allow(non_camel_case_types)]
type PioM_N = iopctl::Pio0_0;
/// Pin function number.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Function {
/// Function 0
F0,
/// Function 1
F1,
/// Function 2
F2,
/// Function 3
F3,
/// Function 4
F4,
/// Function 5
F5,
/// Function 6
F6,
/// Function 7
F7,
/// Function 8
F8,
}
/// Internal pull-up/down resistors on a pin.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Pull {
/// No pull-up or pull-down resistor selected
None,
/// Pull-up resistor
Up,
/// Pull-down resistor
Down,
}
/// Pin slew rate.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SlewRate {
/// Standard slew rate
Standard,
/// Slow slew rate
Slow,
}
/// Output drive strength of a pin.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DriveStrength {
/// Normal
Normal,
/// Full
Full,
}
/// Output drive mode of a pin.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DriveMode {
/// Push-Pull
PushPull,
/// Pseudo Open-Drain
OpenDrain,
}
/// Input inverter of a pin.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Inverter {
/// No inverter
Disabled,
/// Enable input inverter on the input port. A low signal will be
/// seen as a high signal by the pin.
Enabled,
}
trait SealedPin {}
trait ToAnyPin: SealedPin {
#[inline]
fn to_raw(port: u8, pin: u8) -> AnyPin {
// SAFETY: This is safe since this is only called from within the module,
// where the port and pin numbers have been verified to be correct.
unsafe { AnyPin::steal(port, pin) }
}
}
trait ToFC15Pin: SealedPin {
#[inline]
fn to_raw(pin: u8) -> FC15Pin {
// SAFETY: This is safe since this is only called from within the module,
// where the port and pin numbers have been verified to be correct.
unsafe { FC15Pin::steal(pin) }
}
}
/// A pin that can be configured via iopctl.
#[allow(private_bounds)]
pub trait IopctlPin: SealedPin {
/// Sets the function number of a pin.
///
/// This number corresponds to a specific function that the pin supports.
///
/// Typically, function 0 corresponds to GPIO while other numbers correspond to a special function.
///
/// See Section 7.5.3 in reference manual for list of pins and their supported functions.
fn set_function(&self, function: Function) -> &Self;
/// Enables either a pull-up or pull-down resistor on a pin.
///
/// Setting this to [`Pull::None`] will disable the resistor.
fn set_pull(&self, pull: Pull) -> &Self;
/// Enables the input buffer of a pin.
///
/// This must be enabled for any pin acting as an input,
/// and some peripheral pins acting as output may need this enabled as well.
///
/// If there is any doubt, it is best to enable the input buffer.
///
/// See Section 7.4.2.3 of reference manual.
fn enable_input_buffer(&self) -> &Self;
/// Disables the input buffer of a pin.
fn disable_input_buffer(&self) -> &Self;
/// Sets the slew rate of a pin.
///
/// This controls the speed at which a pin can toggle,
/// which is voltage and load dependent.
fn set_slew_rate(&self, slew_rate: SlewRate) -> &Self;
/// Sets the output drive strength of a pin.
///
/// A drive strength of [`DriveStrength::Full`] has twice the
/// high and low drive capability of the [`DriveStrength::Normal`] setting.
fn set_drive_strength(&self, strength: DriveStrength) -> &Self;
/// Enables the analog multiplexer of a pin.
///
/// This must be called to allow analog functionalities of a pin.
///
/// To protect the analog input, [`IopctlPin::set_function`] should be
/// called with [`Function::F0`] to disable digital functions.
///
/// Additionally, [`IopctlPin::disable_input_buffer`] and [`IopctlPin::set_pull`]
/// with [`Pull::None`] should be called.
fn enable_analog_multiplex(&self) -> &Self;
/// Disables the analog multiplexer of a pin.
fn disable_analog_multiplex(&self) -> &Self;
/// Sets the ouput drive mode of a pin.
///
/// A pin configured as [`DriveMode::OpenDrain`] actually operates in
/// a "pseudo" open-drain mode which is somewhat different than true open-drain.
///
/// See Section 7.4.2.7 of reference manual.
fn set_drive_mode(&self, mode: DriveMode) -> &Self;
/// Sets the input inverter of an input pin.
///
/// Setting this to [`Inverter::Enabled`] will invert
/// the input signal.
fn set_input_inverter(&self, inverter: Inverter) -> &Self;
/// Returns a pin to its reset state.
fn reset(&self) -> &Self;
}
/// Represents a pin peripheral created at run-time from given port and pin numbers.
pub struct AnyPin {
pin_port: u8,
reg: &'static PioM_N,
}
impl AnyPin {
/// Creates a pin from raw port and pin numbers which can then be configured.
///
/// This should ONLY be called when there is no other choice
/// (e.g. from a type-erased GPIO pin).
///
/// Otherwise, pin peripherals should be configured directly.
///
/// # Safety
///
/// The caller MUST ensure valid port and pin numbers are provided,
/// and that multiple instances of [`AnyPin`] with the same port
/// and pin combination are not being used simultaneously.
///
/// Failure to uphold these requirements will result in undefined behavior.
///
/// See Table 297 in reference manual for a list of valid
/// pin and port number combinations.
#[must_use]
pub unsafe fn steal(port: u8, pin: u8) -> Self {
// Calculates the offset from the beginning of the IOPCTL register block
// address to the register address representing the pin.
//
// See Table 297 in reference manual for how this offset is calculated.
let offset = ((port as usize) << 7) + ((pin as usize) << 2);
// 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).cast() };
Self {
pin_port: port * 32 + pin,
reg,
}
}
/// Returns the pin's port and pin combination.
#[must_use]
pub fn pin_port(&self) -> usize {
self.pin_port as usize
}
}
/// Represents a FC15 pin peripheral created at run-time from given pin number.
pub struct FC15Pin {
reg: &'static PioM_N,
}
impl FC15Pin {
/// Creates an FC15 pin from raw pin number which can then be configured.
///
/// This should ONLY be called when there is no other choice
/// (e.g. from a type-erased GPIO pin).
///
/// Otherwise, pin peripherals should be configured directly.
///
/// # Safety
///
/// The caller MUST ensure valid port and pin numbers are provided,
/// and that multiple instances of [`AnyPin`] with the same port
/// and pin combination are not being used simultaneously.
///
/// Failure to uphold these requirements will result in undefined behavior.
///
/// See Table 297 in reference manual for a list of valid
/// pin and port number combinations.
#[must_use]
pub unsafe fn steal(pin: u8) -> Self {
// Table 297: FC15_I2C_SCL offset = 0x400, FC15_I2C_SCL offset = 0x404
let iopctl = unsafe { crate::pac::Iopctl::steal() };
let reg = if pin == 0 {
&*iopctl.fc15_i2c_scl().as_ptr().cast()
} else {
&*iopctl.fc15_i2c_sda().as_ptr().cast()
};
Self { reg }
}
}
// This allows AnyPin/FC15Pin 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.
embassy_hal_internal::impl_peripheral!(AnyPin);
impl SealedPin for AnyPin {}
embassy_hal_internal::impl_peripheral!(FC15Pin);
impl SealedPin for FC15Pin {}
macro_rules! impl_iopctlpin {
($pintype:ident) => {
impl IopctlPin for $pintype {
fn set_function(&self, function: Function) -> &Self {
critical_section::with(|_| match function {
Function::F0 => {
self.reg.modify(|_, w| w.fsel().function_0());
}
Function::F1 => {
self.reg.modify(|_, w| w.fsel().function_1());
}
Function::F2 => {
self.reg.modify(|_, w| w.fsel().function_2());
}
Function::F3 => {
self.reg.modify(|_, w| w.fsel().function_3());
}
Function::F4 => {
self.reg.modify(|_, w| w.fsel().function_4());
}
Function::F5 => {
self.reg.modify(|_, w| w.fsel().function_5());
}
Function::F6 => {
self.reg.modify(|_, w| w.fsel().function_6());
}
Function::F7 => {
self.reg.modify(|_, w| w.fsel().function_7());
}
Function::F8 => {
self.reg.modify(|_, w| w.fsel().function_8());
}
});
self
}
fn set_pull(&self, pull: Pull) -> &Self {
critical_section::with(|_| {
match pull {
Pull::None => {
self.reg.modify(|_, w| w.pupdena().disabled());
}
Pull::Up => {
self.reg.modify(|_, w| w.pupdena().enabled().pupdsel().pull_up());
}
Pull::Down => {
self.reg
.modify(|_, w| w.pupdena().enabled().pupdsel().pull_down());
}
}
self
})
}
fn enable_input_buffer(&self) -> &Self {
critical_section::with(|_| self.reg.modify(|_, w| w.ibena().enabled()));
self
}
fn disable_input_buffer(&self) -> &Self {
critical_section::with(|_| self.reg.modify(|_, w| w.ibena().disabled()));
self
}
fn set_slew_rate(&self, slew_rate: SlewRate) -> &Self {
critical_section::with(|_| match slew_rate {
SlewRate::Standard => {
self.reg.modify(|_, w| w.slewrate().normal());
}
SlewRate::Slow => {
self.reg.modify(|_, w| w.slewrate().slow());
}
});
self
}
fn set_drive_strength(&self, strength: DriveStrength) -> &Self {
critical_section::with(|_| match strength {
DriveStrength::Normal => {
self.reg.modify(|_, w| w.fulldrive().normal_drive());
}
DriveStrength::Full => {
self.reg.modify(|_, w| w.fulldrive().full_drive());
}
});
self
}
fn enable_analog_multiplex(&self) -> &Self {
critical_section::with(|_| self.reg.modify(|_, w| w.amena().enabled()));
self
}
fn disable_analog_multiplex(&self) -> &Self {
critical_section::with(|_| self.reg.modify(|_, w| w.amena().disabled()));
self
}
fn set_drive_mode(&self, mode: DriveMode) -> &Self {
critical_section::with(|_| match mode {
DriveMode::PushPull => {
self.reg.modify(|_, w| w.odena().disabled());
}
DriveMode::OpenDrain => {
self.reg.modify(|_, w| w.odena().enabled());
}
});
self
}
fn set_input_inverter(&self, inverter: Inverter) -> &Self {
critical_section::with(|_| match inverter {
Inverter::Disabled => {
self.reg.modify(|_, w| w.iiena().disabled());
}
Inverter::Enabled => {
self.reg.modify(|_, w| w.iiena().enabled());
}
});
self
}
fn reset(&self) -> &Self {
self.reg.reset();
self
}
}
};
}
impl_iopctlpin!(AnyPin);
impl_iopctlpin!(FC15Pin);
macro_rules! impl_FC15pin {
($pin_periph:ident, $pin_no:expr) => {
impl SealedPin for crate::peripherals::$pin_periph {}
impl ToFC15Pin for crate::peripherals::$pin_periph {}
impl IopctlPin for crate::peripherals::$pin_periph {
#[inline]
fn set_function(&self, _function: Function) -> &Self {
//No function configuration for FC15 pin
self
}
#[inline]
fn set_pull(&self, pull: Pull) -> &Self {
Self::to_raw($pin_no).set_pull(pull);
self
}
#[inline]
fn enable_input_buffer(&self) -> &Self {
Self::to_raw($pin_no).enable_input_buffer();
self
}
#[inline]
fn disable_input_buffer(&self) -> &Self {
Self::to_raw($pin_no).disable_input_buffer();
self
}
#[inline]
fn set_slew_rate(&self, slew_rate: SlewRate) -> &Self {
Self::to_raw($pin_no).set_slew_rate(slew_rate);
self
}
#[inline]
fn set_drive_strength(&self, strength: DriveStrength) -> &Self {
Self::to_raw($pin_no).set_drive_strength(strength);
self
}
#[inline]
fn enable_analog_multiplex(&self) -> &Self {
Self::to_raw($pin_no).enable_analog_multiplex();
self
}
#[inline]
fn disable_analog_multiplex(&self) -> &Self {
Self::to_raw($pin_no).disable_analog_multiplex();
self
}
#[inline]
fn set_drive_mode(&self, mode: DriveMode) -> &Self {
Self::to_raw($pin_no).set_drive_mode(mode);
self
}
#[inline]
fn set_input_inverter(&self, inverter: Inverter) -> &Self {
Self::to_raw($pin_no).set_input_inverter(inverter);
self
}
#[inline]
fn reset(&self) -> &Self {
Self::to_raw($pin_no).reset();
self
}
}
};
}
macro_rules! impl_pin {
($pin_periph:ident, $pin_port:expr, $pin_no:expr) => {
impl SealedPin for crate::peripherals::$pin_periph {}
impl ToAnyPin for crate::peripherals::$pin_periph {}
impl IopctlPin for crate::peripherals::$pin_periph {
#[inline]
fn set_function(&self, function: Function) -> &Self {
Self::to_raw($pin_port, $pin_no).set_function(function);
self
}
#[inline]
fn set_pull(&self, pull: Pull) -> &Self {
Self::to_raw($pin_port, $pin_no).set_pull(pull);
self
}
#[inline]
fn enable_input_buffer(&self) -> &Self {
Self::to_raw($pin_port, $pin_no).enable_input_buffer();
self
}
#[inline]
fn disable_input_buffer(&self) -> &Self {
Self::to_raw($pin_port, $pin_no).disable_input_buffer();
self
}
#[inline]
fn set_slew_rate(&self, slew_rate: SlewRate) -> &Self {
Self::to_raw($pin_port, $pin_no).set_slew_rate(slew_rate);
self
}
#[inline]
fn set_drive_strength(&self, strength: DriveStrength) -> &Self {
Self::to_raw($pin_port, $pin_no).set_drive_strength(strength);
self
}
#[inline]
fn enable_analog_multiplex(&self) -> &Self {
Self::to_raw($pin_port, $pin_no).enable_analog_multiplex();
self
}
#[inline]
fn disable_analog_multiplex(&self) -> &Self {
Self::to_raw($pin_port, $pin_no).disable_analog_multiplex();
self
}
#[inline]
fn set_drive_mode(&self, mode: DriveMode) -> &Self {
Self::to_raw($pin_port, $pin_no).set_drive_mode(mode);
self
}
#[inline]
fn set_input_inverter(&self, inverter: Inverter) -> &Self {
Self::to_raw($pin_port, $pin_no).set_input_inverter(inverter);
self
}
#[inline]
fn reset(&self) -> &Self {
Self::to_raw($pin_port, $pin_no).reset();
self
}
}
};
}
impl_pin!(PIO0_0, 0, 0);
impl_pin!(PIO0_1, 0, 1);
impl_pin!(PIO0_2, 0, 2);
impl_pin!(PIO0_3, 0, 3);
impl_pin!(PIO0_4, 0, 4);
impl_pin!(PIO0_5, 0, 5);
impl_pin!(PIO0_6, 0, 6);
impl_pin!(PIO0_7, 0, 7);
impl_pin!(PIO0_8, 0, 8);
impl_pin!(PIO0_9, 0, 9);
impl_pin!(PIO0_10, 0, 10);
impl_pin!(PIO0_11, 0, 11);
impl_pin!(PIO0_12, 0, 12);
impl_pin!(PIO0_13, 0, 13);
impl_pin!(PIO0_14, 0, 14);
impl_pin!(PIO0_15, 0, 15);
impl_pin!(PIO0_16, 0, 16);
impl_pin!(PIO0_17, 0, 17);
impl_pin!(PIO0_18, 0, 18);
impl_pin!(PIO0_19, 0, 19);
impl_pin!(PIO0_20, 0, 20);
impl_pin!(PIO0_21, 0, 21);
impl_pin!(PIO0_22, 0, 22);
impl_pin!(PIO0_23, 0, 23);
impl_pin!(PIO0_24, 0, 24);
impl_pin!(PIO0_25, 0, 25);
impl_pin!(PIO0_26, 0, 26);
impl_pin!(PIO0_27, 0, 27);
impl_pin!(PIO0_28, 0, 28);
impl_pin!(PIO0_29, 0, 29);
impl_pin!(PIO0_30, 0, 30);
impl_pin!(PIO0_31, 0, 31);
impl_pin!(PIO1_0, 1, 0);
impl_pin!(PIO1_1, 1, 1);
impl_pin!(PIO1_2, 1, 2);
impl_pin!(PIO1_3, 1, 3);
impl_pin!(PIO1_4, 1, 4);
impl_pin!(PIO1_5, 1, 5);
impl_pin!(PIO1_6, 1, 6);
impl_pin!(PIO1_7, 1, 7);
impl_pin!(PIO1_8, 1, 8);
impl_pin!(PIO1_9, 1, 9);
impl_pin!(PIO1_10, 1, 10);
impl_pin!(PIO1_11, 1, 11);
impl_pin!(PIO1_12, 1, 12);
impl_pin!(PIO1_13, 1, 13);
impl_pin!(PIO1_14, 1, 14);
impl_pin!(PIO1_15, 1, 15);
impl_pin!(PIO1_16, 1, 16);
impl_pin!(PIO1_17, 1, 17);
impl_pin!(PIO1_18, 1, 18);
impl_pin!(PIO1_19, 1, 19);
impl_pin!(PIO1_20, 1, 20);
impl_pin!(PIO1_21, 1, 21);
impl_pin!(PIO1_22, 1, 22);
impl_pin!(PIO1_23, 1, 23);
impl_pin!(PIO1_24, 1, 24);
impl_pin!(PIO1_25, 1, 25);
impl_pin!(PIO1_26, 1, 26);
impl_pin!(PIO1_27, 1, 27);
impl_pin!(PIO1_28, 1, 28);
impl_pin!(PIO1_29, 1, 29);
impl_pin!(PIO1_30, 1, 30);
impl_pin!(PIO1_31, 1, 31);
impl_pin!(PIO2_0, 2, 0);
impl_pin!(PIO2_1, 2, 1);
impl_pin!(PIO2_2, 2, 2);
impl_pin!(PIO2_3, 2, 3);
impl_pin!(PIO2_4, 2, 4);
impl_pin!(PIO2_5, 2, 5);
impl_pin!(PIO2_6, 2, 6);
impl_pin!(PIO2_7, 2, 7);
impl_pin!(PIO2_8, 2, 8);
impl_pin!(PIO2_9, 2, 9);
impl_pin!(PIO2_10, 2, 10);
impl_pin!(PIO2_11, 2, 11);
impl_pin!(PIO2_12, 2, 12);
impl_pin!(PIO2_13, 2, 13);
impl_pin!(PIO2_14, 2, 14);
impl_pin!(PIO2_15, 2, 15);
impl_pin!(PIO2_16, 2, 16);
impl_pin!(PIO2_17, 2, 17);
impl_pin!(PIO2_18, 2, 18);
impl_pin!(PIO2_19, 2, 19);
impl_pin!(PIO2_20, 2, 20);
impl_pin!(PIO2_21, 2, 21);
impl_pin!(PIO2_22, 2, 22);
impl_pin!(PIO2_23, 2, 23);
impl_pin!(PIO2_24, 2, 24);
// Note: These have have reset values of 0x41 to support SWD by default
impl_pin!(PIO2_25, 2, 25);
impl_pin!(PIO2_26, 2, 26);
impl_pin!(PIO2_27, 2, 27);
impl_pin!(PIO2_28, 2, 28);
impl_pin!(PIO2_29, 2, 29);
impl_pin!(PIO2_30, 2, 30);
impl_pin!(PIO2_31, 2, 31);
impl_pin!(PIO3_0, 3, 0);
impl_pin!(PIO3_1, 3, 1);
impl_pin!(PIO3_2, 3, 2);
impl_pin!(PIO3_3, 3, 3);
impl_pin!(PIO3_4, 3, 4);
impl_pin!(PIO3_5, 3, 5);
impl_pin!(PIO3_6, 3, 6);
impl_pin!(PIO3_7, 3, 7);
impl_pin!(PIO3_8, 3, 8);
impl_pin!(PIO3_9, 3, 9);
impl_pin!(PIO3_10, 3, 10);
impl_pin!(PIO3_11, 3, 11);
impl_pin!(PIO3_12, 3, 12);
impl_pin!(PIO3_13, 3, 13);
impl_pin!(PIO3_14, 3, 14);
impl_pin!(PIO3_15, 3, 15);
impl_pin!(PIO3_16, 3, 16);
impl_pin!(PIO3_17, 3, 17);
impl_pin!(PIO3_18, 3, 18);
impl_pin!(PIO3_19, 3, 19);
impl_pin!(PIO3_20, 3, 20);
impl_pin!(PIO3_21, 3, 21);
impl_pin!(PIO3_22, 3, 22);
impl_pin!(PIO3_23, 3, 23);
impl_pin!(PIO3_24, 3, 24);
impl_pin!(PIO3_25, 3, 25);
impl_pin!(PIO3_26, 3, 26);
impl_pin!(PIO3_27, 3, 27);
impl_pin!(PIO3_28, 3, 28);
impl_pin!(PIO3_29, 3, 29);
impl_pin!(PIO3_30, 3, 30);
impl_pin!(PIO3_31, 3, 31);
impl_pin!(PIO4_0, 4, 0);
impl_pin!(PIO4_1, 4, 1);
impl_pin!(PIO4_2, 4, 2);
impl_pin!(PIO4_3, 4, 3);
impl_pin!(PIO4_4, 4, 4);
impl_pin!(PIO4_5, 4, 5);
impl_pin!(PIO4_6, 4, 6);
impl_pin!(PIO4_7, 4, 7);
impl_pin!(PIO4_8, 4, 8);
impl_pin!(PIO4_9, 4, 9);
impl_pin!(PIO4_10, 4, 10);
impl_pin!(PIO7_24, 7, 24);
impl_pin!(PIO7_25, 7, 25);
impl_pin!(PIO7_26, 7, 26);
impl_pin!(PIO7_27, 7, 27);
impl_pin!(PIO7_28, 7, 28);
impl_pin!(PIO7_29, 7, 29);
impl_pin!(PIO7_30, 7, 30);
impl_pin!(PIO7_31, 7, 31);
// FC15 pins
impl_FC15pin!(PIOFC15_SCL, 0);
impl_FC15pin!(PIOFC15_SDA, 1);