diff --git a/src/chips/mimxrt633s.rs b/src/chips/mimxrt633s.rs index 4c326344..30072132 100644 --- a/src/chips/mimxrt633s.rs +++ b/src/chips/mimxrt633s.rs @@ -363,6 +363,8 @@ embassy_hal_internal::peripherals!( PIO7_29, PIO7_30, PIO7_31, + PIOFC15_SCL, + PIOFC15_SDA, PMC_PMIC, PIMCTL, POWERQUAD, diff --git a/src/chips/mimxrt685s.rs b/src/chips/mimxrt685s.rs index 873779f5..746861e3 100644 --- a/src/chips/mimxrt685s.rs +++ b/src/chips/mimxrt685s.rs @@ -362,6 +362,8 @@ embassy_hal_internal::peripherals!( PIO7_29, PIO7_30, PIO7_31, + PIOFC15_SCL, + PIOFC15_SDA, PMC_PMIC, PIMCTL, POWERQUAD, diff --git a/src/flexcomm.rs b/src/flexcomm.rs index 7341f873..f82d1d5f 100644 --- a/src/flexcomm.rs +++ b/src/flexcomm.rs @@ -5,7 +5,9 @@ use paste::paste; use crate::clocks::{enable_and_reset, SysconPeripheral}; use crate::pac; -use crate::peripherals::{FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7}; +use crate::peripherals::{ + FLEXCOMM0, FLEXCOMM1, FLEXCOMM14, FLEXCOMM15, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, +}; /// clock selection option #[derive(Copy, Clone, Debug)] @@ -22,8 +24,17 @@ pub enum Clock { /// MASTER Master, - /// `FCn_FRG` - FcnFrg, + /// FCn_FRG with Main clock source + FcnFrgMain, + + /// FCn_FRG with Pll clock source + FcnFrgPll, + + /// FCn_FRG with Sfro clock source + FcnFrgSfro, + + /// FCn_FRG with Ffro clock source + FcnFrgFfro, /// disabled None, @@ -70,17 +81,20 @@ macro_rules! impl_flexcomm { Clock::Ffro => w.sel().ffro_clk(), Clock::AudioPll => w.sel().audio_pll_clk(), Clock::Master => w.sel().master_clk(), - Clock::FcnFrg => w.sel().fcn_frg_clk(), - Clock::None => w.sel().none(), - }); + Clock::FcnFrgMain => w.sel().fcn_frg_clk(), + Clock::FcnFrgPll => w.sel().fcn_frg_clk(), + Clock::FcnFrgSfro => w.sel().fcn_frg_clk(), + Clock::FcnFrgFfro => w.sel().fcn_frg_clk(), + Clock::None => w.sel().none(), // no clock? throw an error? + }); clkctl1.flexcomm($idx).frgclksel().write(|w| match clk { - Clock::Sfro => w.sel().sfro_clk(), - Clock::Ffro => w.sel().ffro_clk(), - Clock::AudioPll => w.sel().frg_pll_clk(), - Clock::Master => w.sel().main_clk(), - Clock::FcnFrg => w.sel().frg_pll_clk(), - Clock::None => w.sel().none(), + Clock::FcnFrgMain => w.sel().main_clk(), + Clock::FcnFrgPll => w.sel().frg_pll_clk(), + Clock::FcnFrgSfro => w.sel().sfro_clk(), + Clock::FcnFrgFfro => w.sel().ffro_clk(), + _ => w.sel().none(), // not using frg ... }); + // todo: add support for frg div/mult clkctl1 .flexcomm($idx) .frgctl() @@ -98,6 +112,89 @@ macro_rules! impl_flexcomm { impl_flexcomm!(0, 1, 2, 3, 4, 5, 6, 7); +// TODO: FLEXCOMM 14 is untested. Enable SPI support on FLEXCOMM14 +// Add special case FLEXCOMM14 +impl sealed::Sealed for crate::peripherals::FLEXCOMM14 {} + +impl FlexcommLowLevel for crate::peripherals::FLEXCOMM14 { + fn reg() -> &'static crate::pac::flexcomm0::RegisterBlock { + // SAFETY: safe from single executor, enforce + // via peripheral reference lifetime tracking + unsafe { &*crate::pac::Flexcomm14::ptr() } + } + + fn enable(clk: Clock) { + // SAFETY: safe from single executor + let clkctl1 = unsafe { crate::pac::Clkctl1::steal() }; + + clkctl1.fc14fclksel().write(|w| match clk { + Clock::Sfro => w.sel().sfro_clk(), + Clock::Ffro => w.sel().ffro_clk(), + Clock::AudioPll => w.sel().audio_pll_clk(), + Clock::Master => w.sel().master_clk(), + Clock::FcnFrgMain => w.sel().fcn_frg_clk(), + Clock::FcnFrgPll => w.sel().fcn_frg_clk(), + Clock::FcnFrgSfro => w.sel().fcn_frg_clk(), + Clock::FcnFrgFfro => w.sel().fcn_frg_clk(), + Clock::None => w.sel().none(), // no clock? throw an error? + }); + clkctl1.frg14clksel().write(|w| match clk { + Clock::FcnFrgMain => w.sel().main_clk(), + Clock::FcnFrgPll => w.sel().frg_pll_clk(), + Clock::FcnFrgSfro => w.sel().sfro_clk(), + Clock::FcnFrgFfro => w.sel().ffro_clk(), + _ => w.sel().none(), // not using frg ... + }); + // todo: add support for frg div/mult + clkctl1.frg14ctl().write(|w| + // SAFETY: unsafe only used for .bits() call + unsafe { w.mult().bits(0) }); + + enable_and_reset::(); + } +} + +// Add special case FLEXCOMM15 +impl sealed::Sealed for crate::peripherals::FLEXCOMM15 {} + +impl FlexcommLowLevel for crate::peripherals::FLEXCOMM15 { + fn reg() -> &'static crate::pac::flexcomm0::RegisterBlock { + // SAFETY: safe from single executor, enforce + // via peripheral reference lifetime tracking + unsafe { &*crate::pac::Flexcomm15::ptr() } + } + + fn enable(clk: Clock) { + // SAFETY: safe from single executor + let clkctl1 = unsafe { crate::pac::Clkctl1::steal() }; + + clkctl1.fc15fclksel().write(|w| match clk { + Clock::Sfro => w.sel().sfro_clk(), + Clock::Ffro => w.sel().ffro_clk(), + Clock::AudioPll => w.sel().audio_pll_clk(), + Clock::Master => w.sel().master_clk(), + Clock::FcnFrgMain => w.sel().fcn_frg_clk(), + Clock::FcnFrgPll => w.sel().fcn_frg_clk(), + Clock::FcnFrgSfro => w.sel().fcn_frg_clk(), + Clock::FcnFrgFfro => w.sel().fcn_frg_clk(), + Clock::None => w.sel().none(), // no clock? throw an error? + }); + clkctl1.frg15clksel().write(|w| match clk { + Clock::FcnFrgMain => w.sel().main_clk(), + Clock::FcnFrgPll => w.sel().frg_pll_clk(), + Clock::FcnFrgSfro => w.sel().sfro_clk(), + Clock::FcnFrgFfro => w.sel().ffro_clk(), + _ => w.sel().none(), // not using frg ... + }); + // todo: add support for frg div/mult + clkctl1.frg15ctl().write(|w| + // SAFETY: unsafe only used for .bits() call + unsafe { w.mult().bits(0) }); + + enable_and_reset::(); + } +} + macro_rules! declare_into_mode { ($mode:ident) => { paste! { @@ -130,13 +227,15 @@ macro_rules! impl_into_mode { declare_into_mode!(usart); impl_into_mode!(usart, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7); -// REVISIT: Add support for FLEXCOMM14 declare_into_mode!(spi); -impl_into_mode!(spi, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7); +impl_into_mode!( + spi, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, FLEXCOMM14 +); -// REVISIT: Add support for FLEXCOMM15 declare_into_mode!(i2c); -impl_into_mode!(i2c, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7); +impl_into_mode!( + i2c, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, FLEXCOMM15 +); declare_into_mode!(i2s_transmit); impl_into_mode!( @@ -163,7 +262,3 @@ impl_into_mode!( FLEXCOMM6, FLEXCOMM7 ); - -// TODO: in follow up flexcomm PR, implement special FC14 + FC15 support -//impl_flexcomm!(14, FLEXCOMM14, Flexcomm14, I2c14, Spi14, I2s14); -//impl_flexcomm!(15, FLEXCOMM15, Flexcomm15, I2c15, Sp157, I2s15); diff --git a/src/i2c/mod.rs b/src/i2c/mod.rs index 6e0e46aa..62d84b46 100644 --- a/src/i2c/mod.rs +++ b/src/i2c/mod.rs @@ -97,34 +97,42 @@ pub trait Instance: crate::flexcomm::IntoI2c + SealedInstance + Peripheral

{ - $( - paste!{ - impl SealedInstance for crate::peripherals::[] { - fn info() -> Info { - Info { - regs: unsafe { &*crate::pac::[]::ptr() }, - index: $n, - } - } - - - #[inline] - fn index() -> usize { - $n - } - } - - impl Instance for crate::peripherals::[] { - type Interrupt = crate::interrupt::typelevel::[]; - } - } - )* + $( + paste!{ + impl SealedInstance for crate::peripherals::[] { + fn info() -> Info { + let mut info_index = $n; + if $n == 15 { + info_index = 8; + } + + Info { + regs: unsafe { &*crate::pac::[]::ptr() }, + index: info_index, + } + } + + + #[inline] + fn index() -> usize { + if $n == 15 { + return 8 + } + $n + } + } + + impl Instance for crate::peripherals::[] { + type Interrupt = crate::interrupt::typelevel::[]; + } + } + )* }; } -impl_instance!(0, 1, 2, 3, 4, 5, 6, 7); +impl_instance!(0, 1, 2, 3, 4, 5, 6, 7, 15); -const I2C_COUNT: usize = 8; +const I2C_COUNT: usize = 9; static I2C_WAKERS: [AtomicWaker; I2C_COUNT] = [const { AtomicWaker::new() }; I2C_COUNT]; /// Ten bit addresses start with first byte 0b11110XXX @@ -293,6 +301,12 @@ impl_sda!(PIO4_2, F1, FLEXCOMM7); impl_sda!(PIO4_3, F1, FLEXCOMM7); impl_scl!(PIO4_4, F1, FLEXCOMM7); +// Flexcomm15 GPIOs +// Function configuration is not needed for FC15 +// Implementing SCL/SDA traits to use the I2C APIs +impl_scl!(PIOFC15_SCL, F1, FLEXCOMM15); +impl_sda!(PIOFC15_SDA, F1, FLEXCOMM15); + /// I2C Master DMA trait. #[allow(private_bounds)] pub trait MasterDma: dma::Instance {} diff --git a/src/iopctl.rs b/src/iopctl.rs index 81aa27c5..6a226452 100644 --- a/src/iopctl.rs +++ b/src/iopctl.rs @@ -101,6 +101,15 @@ trait ToAnyPin: SealedPin { } } +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::new(pin) } + } +} + /// A pin that can be configured via iopctl. #[allow(private_bounds)] pub trait IopctlPin: SealedPin { @@ -222,133 +231,258 @@ impl AnyPin { } } -// This allows AnyPin to be used in HAL constructors that require types +/// 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 new(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 {} -impl IopctlPin for AnyPin { - fn set_function(&self, function: Function) -> &Self { - match function { - Function::F0 => { - self.reg.modify(|_, w| w.fsel().function_0()); - } - Function::F1 => { - self.reg.modify(|_, w| w.fsel().function_1()); + +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 { + 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 } - Function::F2 => { - self.reg.modify(|_, w| w.fsel().function_2()); + + fn set_pull(&self, pull: Pull) -> &Self { + 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 } - Function::F3 => { - self.reg.modify(|_, w| w.fsel().function_3()); + + fn enable_input_buffer(&self) -> &Self { + self.reg.modify(|_, w| w.ibena().enabled()); + self } - Function::F4 => { - self.reg.modify(|_, w| w.fsel().function_4()); + + fn disable_input_buffer(&self) -> &Self { + self.reg.modify(|_, w| w.ibena().disabled()); + self } - Function::F5 => { - self.reg.modify(|_, w| w.fsel().function_5()); + + fn set_slew_rate(&self, slew_rate: SlewRate) -> &Self { + match slew_rate { + SlewRate::Standard => { + self.reg.modify(|_, w| w.slewrate().normal()); + } + SlewRate::Slow => { + self.reg.modify(|_, w| w.slewrate().slow()); + } + } + self } - Function::F6 => { - self.reg.modify(|_, w| w.fsel().function_6()); + + fn set_drive_strength(&self, strength: DriveStrength) -> &Self { + match strength { + DriveStrength::Normal => { + self.reg.modify(|_, w| w.fulldrive().normal_drive()); + } + DriveStrength::Full => { + self.reg.modify(|_, w| w.fulldrive().full_drive()); + } + } + self } - Function::F7 => { - self.reg.modify(|_, w| w.fsel().function_7()); + + fn enable_analog_multiplex(&self) -> &Self { + self.reg.modify(|_, w| w.amena().enabled()); + self } - Function::F8 => { - self.reg.modify(|_, w| w.fsel().function_8()); + + fn disable_analog_multiplex(&self) -> &Self { + self.reg.modify(|_, w| w.amena().disabled()); + self } - } - self - } - fn set_pull(&self, pull: Pull) -> &Self { - match pull { - Pull::None => { - self.reg.modify(|_, w| w.pupdena().disabled()); + fn set_drive_mode(&self, mode: DriveMode) -> &Self { + match mode { + DriveMode::PushPull => { + self.reg.modify(|_, w| w.odena().disabled()); + } + DriveMode::OpenDrain => { + self.reg.modify(|_, w| w.odena().enabled()); + } + } + self } - Pull::Up => { - self.reg.modify(|_, w| w.pupdena().enabled().pupdsel().pull_up()); + + fn set_input_inverter(&self, inverter: Inverter) -> &Self { + match inverter { + Inverter::Disabled => { + self.reg.modify(|_, w| w.iiena().disabled()); + } + Inverter::Enabled => { + self.reg.modify(|_, w| w.iiena().enabled()); + } + } + self } - Pull::Down => { - self.reg.modify(|_, w| w.pupdena().enabled().pupdsel().pull_down()); + + fn reset(&self) -> &Self { + self.reg.reset(); + self } } - self - } + }; +} - fn enable_input_buffer(&self) -> &Self { - self.reg.modify(|_, w| w.ibena().enabled()); - self - } +impl_iopctlpin!(AnyPin); +impl_iopctlpin!(FC15Pin); - fn disable_input_buffer(&self) -> &Self { - self.reg.modify(|_, w| w.ibena().disabled()); - self - } +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 + } - fn set_slew_rate(&self, slew_rate: SlewRate) -> &Self { - match slew_rate { - SlewRate::Standard => { - self.reg.modify(|_, w| w.slewrate().normal()); + #[inline] + fn set_pull(&self, pull: Pull) -> &Self { + Self::to_raw($pin_no).set_pull(pull); + self } - SlewRate::Slow => { - self.reg.modify(|_, w| w.slewrate().slow()); + + #[inline] + fn enable_input_buffer(&self) -> &Self { + Self::to_raw($pin_no).enable_input_buffer(); + self } - } - self - } - fn set_drive_strength(&self, strength: DriveStrength) -> &Self { - match strength { - DriveStrength::Normal => { - self.reg.modify(|_, w| w.fulldrive().normal_drive()); + #[inline] + fn disable_input_buffer(&self) -> &Self { + Self::to_raw($pin_no).disable_input_buffer(); + self } - DriveStrength::Full => { - self.reg.modify(|_, w| w.fulldrive().full_drive()); + + #[inline] + fn set_slew_rate(&self, slew_rate: SlewRate) -> &Self { + Self::to_raw($pin_no).set_slew_rate(slew_rate); + self } - } - self - } - fn enable_analog_multiplex(&self) -> &Self { - self.reg.modify(|_, w| w.amena().enabled()); - self - } + #[inline] + fn set_drive_strength(&self, strength: DriveStrength) -> &Self { + Self::to_raw($pin_no).set_drive_strength(strength); + self + } - fn disable_analog_multiplex(&self) -> &Self { - self.reg.modify(|_, w| w.amena().disabled()); - self - } + #[inline] + fn enable_analog_multiplex(&self) -> &Self { + Self::to_raw($pin_no).enable_analog_multiplex(); + self + } - fn set_drive_mode(&self, mode: DriveMode) -> &Self { - match mode { - DriveMode::PushPull => { - self.reg.modify(|_, w| w.odena().disabled()); + #[inline] + fn disable_analog_multiplex(&self) -> &Self { + Self::to_raw($pin_no).disable_analog_multiplex(); + self } - DriveMode::OpenDrain => { - self.reg.modify(|_, w| w.odena().enabled()); + + #[inline] + fn set_drive_mode(&self, mode: DriveMode) -> &Self { + Self::to_raw($pin_no).set_drive_mode(mode); + self } - } - self - } - fn set_input_inverter(&self, inverter: Inverter) -> &Self { - match inverter { - Inverter::Disabled => { - self.reg.modify(|_, w| w.iiena().disabled()); + #[inline] + fn set_input_inverter(&self, inverter: Inverter) -> &Self { + Self::to_raw($pin_no).set_input_inverter(inverter); + self } - Inverter::Enabled => { - self.reg.modify(|_, w| w.iiena().enabled()); + + #[inline] + fn reset(&self) -> &Self { + Self::to_raw($pin_no).reset(); + self } } - self - } - - fn reset(&self) -> &Self { - self.reg.reset(); - self - } + }; } macro_rules! impl_pin { @@ -575,3 +709,7 @@ 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);