aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorIan McIntyre <me@mciantyre.dev>2025-11-30 18:52:34 -0500
committerIan McIntyre <me@mciantyre.dev>2025-11-30 19:10:51 -0500
commit76199f21616ad86cf68f3b063c1ce23c6fc5a52f (patch)
tree4c076d0afd649803a2bd9a5ed5cbb1f1c74fb459 /drivers
First commit
Diffstat (limited to 'drivers')
-rw-r--r--drivers/ccm-10xx/Cargo.toml8
-rw-r--r--drivers/ccm-10xx/src/ahb.rs103
-rw-r--r--drivers/ccm-10xx/src/ccm.rs805
-rw-r--r--drivers/ccm-10xx/src/ccm_analog.rs244
-rw-r--r--drivers/ccm-10xx/src/lib.rs18
-rw-r--r--drivers/ccm-10xx/src/ral/ccm.rs85
-rw-r--r--drivers/ccm-10xx/src/ral/ccm_analog.rs129
-rw-r--r--drivers/ccm-11xx/Cargo.toml7
-rw-r--r--drivers/ccm-11xx/src/lib.rs465
-rw-r--r--drivers/ccm-11xx/src/ral_1180/clock_root.rs61
-rw-r--r--drivers/ccm-11xx/src/ral_1180/lpcg.rs15
-rw-r--r--drivers/ccm-11xx/src/ral_1180/observe.rs31
-rw-r--r--drivers/ccm-11xx/src/ral_1180/osc_pll.rs15
-rw-r--r--drivers/ccm-11xx/src/ral_11xx/clock_group.rs33
-rw-r--r--drivers/ccm-11xx/src/ral_11xx/clock_root.rs106
-rw-r--r--drivers/ccm-11xx/src/ral_11xx/lpcg.rs28
-rw-r--r--drivers/ccm-11xx/src/ral_11xx/osc_pll.rs173
-rw-r--r--drivers/ccm-11xx/src/ral_11xx/pll.rs598
-rw-r--r--drivers/dcdc/Cargo.toml7
-rw-r--r--drivers/dcdc/src/lib.rs43
-rw-r--r--drivers/edma/Cargo.toml7
-rw-r--r--drivers/edma/src/dma.rs161
-rw-r--r--drivers/edma/src/dmamux.rs16
-rw-r--r--drivers/edma/src/lib.rs990
-rw-r--r--drivers/edma/src/tcd.rs174
-rw-r--r--drivers/enet/Cargo.toml7
-rw-r--r--drivers/enet/src/lib.rs725
-rw-r--r--drivers/flexspi/Cargo.toml7
-rw-r--r--drivers/flexspi/src/lib.rs882
-rw-r--r--drivers/gpc-11xx/Cargo.toml7
-rw-r--r--drivers/gpc-11xx/src/lib.rs381
-rw-r--r--drivers/gpio/Cargo.toml7
-rw-r--r--drivers/gpio/src/lib.rs34
-rw-r--r--drivers/iomuxc-10xx/Cargo.toml7
-rw-r--r--drivers/iomuxc-10xx/src/lib.rs102
-rw-r--r--drivers/iomuxc-11xx/Cargo.toml7
-rw-r--r--drivers/iomuxc-11xx/src/lib.rs475
-rw-r--r--drivers/lpspi/Cargo.toml7
-rw-r--r--drivers/lpspi/src/lib.rs877
-rw-r--r--drivers/pit/Cargo.toml7
-rw-r--r--drivers/pit/src/lib.rs113
-rw-r--r--drivers/pmu-11xx/Cargo.toml7
-rw-r--r--drivers/pmu-11xx/src/lib.rs139
-rw-r--r--drivers/rgpio/Cargo.toml7
-rw-r--r--drivers/rgpio/src/lib.rs164
-rw-r--r--drivers/rtwdog/Cargo.toml7
-rw-r--r--drivers/rtwdog/src/lib.rs95
47 files changed, 8386 insertions, 0 deletions
diff --git a/drivers/ccm-10xx/Cargo.toml b/drivers/ccm-10xx/Cargo.toml
new file mode 100644
index 0000000..962afa6
--- /dev/null
+++ b/drivers/ccm-10xx/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "imxrt-drivers-ccm-10xx"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+defmt = { workspace = true, optional = true }
+ral-registers = { workspace = true }
diff --git a/drivers/ccm-10xx/src/ahb.rs b/drivers/ccm-10xx/src/ahb.rs
new file mode 100644
index 0000000..392be61
--- /dev/null
+++ b/drivers/ccm-10xx/src/ahb.rs
@@ -0,0 +1,103 @@
+//! Routines for configurating AHB clocks.
+
+use crate::{ccm, ccm_analog, ral};
+
+/// The ARM PLL.
+pub mod pll1 {
+ use super::{ccm, ccm_analog, ral};
+
+ /// A configuration for PLL1.
+ pub struct AbhConfiguration {
+ /// The PLL1 divider.
+ pub div_sel: u32,
+ /// The ARM divider, immediately after the PLL.
+ pub arm_divider: u32,
+ /// The AHB divider, downstream of the PLL and ARM divider.
+ pub ahb_divider: u32,
+ }
+
+ impl AbhConfiguration {
+ /// Produces the target AHB frequency expressed by the configuration.
+ pub const fn ahb_frequency(&self) -> u32 {
+ ccm_analog::pll1::frequency(self.div_sel / self.arm_divider / self.ahb_divider)
+ }
+ }
+
+ /// Configure the AHB root using PLL1.
+ ///
+ /// This does not touch any of the clock gates downstream of the root clock.
+ /// You're responsible for managing those. Note that is affects the IPG
+ /// clock; however, it does not affect the IPG divider.
+ pub fn configure_ahb(
+ ccm: ral::ccm::CCM,
+ ccm_analog: ral::ccm_analog::CCM_ANALOG,
+ config: &AbhConfiguration,
+ ) {
+ if ccm::ahb_clk::Selection::PeriphClk2Sel == ccm::ahb_clk::selection(ccm) {
+ // Switch to the pre-peripheral clock before changing
+ // peripheral clock 2...
+ ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PrePeriphClkSel);
+ }
+
+ // Temporarily switch to the crystal oscillator.
+ ccm::periph_clk2::set_divider(ccm, 1);
+ ccm::periph_clk2::set_selection(ccm, ccm::periph_clk2::Selection::Osc);
+ ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PeriphClk2Sel);
+
+ // Prepare PLL1.
+ ccm_analog::pll1::restart(ccm_analog, config.div_sel);
+ ccm::arm_divider::set_divider(ccm, config.arm_divider);
+ ccm::ahb_clk::set_divider(ccm, config.ahb_divider);
+
+ // Switch back to PLL1.
+ ccm::pre_periph_clk_pll1::set_selection(ccm, ccm::pre_periph_clk_pll1::Selection::Pll1);
+ ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PrePeriphClkSel);
+ }
+}
+
+/// PLL6 that's fixed at 500MHz.
+pub mod pll6_500mhz {
+ use super::{ccm, ccm_analog, ral};
+
+ /// Configuration for PLL6 using a 500MHz.
+ pub struct AbhConfiguration {
+ /// The AHB divider, just before the root clock.
+ pub ahb_divider: u32,
+ }
+
+ impl AbhConfiguration {
+ /// Returns the frequency of the root clock.
+ pub const fn ahb_frequency(&self) -> u32 {
+ ccm_analog::pll6_500mhz::FREQUENCY / self.ahb_divider
+ }
+ }
+
+ /// Configure the AHB root using PLL6 fixed at 500MHz.
+ ///
+ /// This does not touch any of the clock gates downstream of the root clock.
+ /// You're responsible for managing those. Note that this affects the IPG
+ /// clock; however, it does not touch the divider.
+ pub fn configure_ahb(
+ ccm: ral::ccm::CCM,
+ ccm_analog: ral::ccm_analog::CCM_ANALOG,
+ config: &AbhConfiguration,
+ ) {
+ if ccm::ahb_clk::Selection::PeriphClk2Sel == ccm::ahb_clk::selection(ccm) {
+ // Switch to the pre-peripheral clock before changing
+ // peripheral clock 2...
+ ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PrePeriphClkSel);
+ }
+
+ // Temporarily switch to the crystal oscillator.
+ ccm::periph_clk2::set_selection(ccm, ccm::periph_clk2::Selection::Osc);
+ ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PeriphClk2Sel);
+
+ // Prepare PLL6.
+ ccm_analog::pll6_500mhz::restart(ccm_analog);
+ ccm::ahb_clk::set_divider(ccm, config.ahb_divider);
+
+ // Switch to PLL6.
+ ccm::pre_periph_clk_pll6::set_selection(ccm, ccm::pre_periph_clk_pll6::Selection::Pll6);
+ ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PrePeriphClkSel);
+ }
+}
diff --git a/drivers/ccm-10xx/src/ccm.rs b/drivers/ccm-10xx/src/ccm.rs
new file mode 100644
index 0000000..6e2e051
--- /dev/null
+++ b/drivers/ccm-10xx/src/ccm.rs
@@ -0,0 +1,805 @@
+//! Clock controller module.
+
+use crate::ral;
+
+pub use ral::ccm::CCM;
+pub type Instance = ral_registers::Instance<ral::ccm::RegisterBlock>;
+
+/// Wait for all handshake bits to deassert.
+fn wait_handshake(ccm: ral::ccm::CCM) {
+ while ral::read_reg!(ral::ccm, ccm, CDHIPR) != 0 {}
+}
+
+/// PERCLK clock.
+///
+/// The PERCLK clock controls GPT and PIT timers.
+pub mod perclk_clk {
+ use crate::ral::{self, ccm::CCM};
+
+ /// PERCLK clock selection.
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// Derive from the IPG clock root.
+ Ipg = 0,
+ /// Derive from the oscillator clock.
+ Oscillator = 1,
+ }
+
+ /// Set the PERCLK clock selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CSCMR1, PERCLK_CLK_SEL: selection as u32);
+ }
+
+ /// Returns the PERCLK clock selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ if ral::read_reg!(ral::ccm, ccm, CSCMR1, PERCLK_CLK_SEL == 1) {
+ Selection::Oscillator
+ } else {
+ Selection::Ipg
+ }
+ }
+
+ /// The smallest PERCLK divider.
+ pub const MIN_DIVIDER: u32 = 1;
+ /// The largest PERCLK divider.
+ pub const MAX_DIVIDER: u32 = 64;
+
+ /// Set the PERCLK clock divider.
+ ///
+ /// The implementation clamps `divider` between [`MIN_DIVIDER`] and [`MAX_DIVIDER`].
+ pub fn set_divider(ccm: CCM, divider: u32) {
+ let podf = divider.clamp(MIN_DIVIDER, MAX_DIVIDER) - 1;
+ ral::modify_reg!(ral::ccm, ccm, CSCMR1, PERCLK_PODF: podf);
+ }
+
+ /// Returns the PERCLK clock divider.
+ pub fn divider(ccm: CCM) -> u32 {
+ ral::read_reg!(ral::ccm, ccm, CSCMR1, PERCLK_PODF) + 1
+ }
+}
+
+/// IPG clock.
+///
+/// The IPG clock is divided from the core clock.
+pub mod ipg_clk {
+ use crate::ral::{self, ccm::CCM};
+
+ /// Returns the IPG clock divider.
+ pub fn divider(ccm: CCM) -> u32 {
+ ral::read_reg!(ral::ccm, ccm, CBCDR, IPG_PODF) + 1
+ }
+
+ /// The smallest IPG divider.
+ pub const MIN_DIVIDER: u32 = 1;
+ /// The largest IPG divider.
+ pub const MAX_DIVIDER: u32 = 4;
+
+ /// Sets the IPG clock divider.
+ ///
+ /// The implementation clamps `divider` between [`MIN_DIVIDER`] and [`MAX_DIVIDER`].
+ pub fn set_divider(ccm: CCM, divider: u32) {
+ let podf = divider.clamp(MIN_DIVIDER, MAX_DIVIDER) - 1;
+ ral::modify_reg!(ral::ccm, ccm, CBCDR, IPG_PODF: podf);
+ }
+}
+
+/// Low power mode.
+///
+/// From the reference manual,
+///
+/// > Setting the low power mode that system will enter on next assertion of dsm_request signal.
+///
+/// Practically, this affects the processor behavior when you use WFI, WFE, or enter another
+/// low-power state. Low-power settings that aren't "run" halt the ARM SYSTICK peripheral.
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(u32)]
+pub enum LowPowerMode {
+ /// Remain in run mode when entering low power.
+ RemainInRun = ral::ccm::CLPCR::LPM::RUN,
+ /// Move to wait mode when entering low power.
+ TransferToWait = ral::ccm::CLPCR::LPM::WAIT,
+ /// Stop when entering low power.
+ TransferToStop = ral::ccm::CLPCR::LPM::STOP,
+}
+
+/// Set the CCM low power mode.
+pub fn set_low_power_mode(ccm: ral::ccm::CCM, mode: LowPowerMode) {
+ ral::modify_reg!(ral::ccm, ccm, CLPCR, LPM: mode as u32);
+}
+
+/// Returns the CCM low power mode.
+///
+/// Returns `None` if the low power mode is the reserved field value.
+pub fn low_power_mode(ccm: ral::ccm::CCM) -> Option<LowPowerMode> {
+ use ral::ccm::CLPCR::LPM::{RUN, STOP, WAIT};
+ Some(match ral::read_reg!(ral::ccm, ccm, CLPCR, LPM) {
+ RUN => LowPowerMode::RemainInRun,
+ WAIT => LowPowerMode::TransferToWait,
+ STOP => LowPowerMode::TransferToStop,
+ _ => return None,
+ })
+}
+
+/// UART clock root.
+///
+/// `uart_clk` provides the clock source for all LPUART peripherals.
+/// You must disable LPUART clock gates before selecting the clock
+/// and divider.
+pub mod uart_clk {
+ use crate::ral::{self, ccm::CCM};
+
+ /// Returns the UART clock divider.
+ pub fn divider(ccm: CCM) -> u32 {
+ ral::read_reg!(ral::ccm, ccm, CSCDR1, UART_CLK_PODF) + 1
+ }
+
+ /// The smallest UART clock divider.
+ pub const MIN_DIVIDER: u32 = 1;
+ /// The largest UART clock divider.
+ pub const MAX_DIVIDER: u32 = 1 << 6;
+
+ /// Set the UART clock divider.
+ ///
+ /// The implementation clamps `divider` between [`MIN_DIVIDER`] and [`MAX_DIVIDER`].
+ pub fn set_divider(ccm: CCM, divider: u32) {
+ let podf = divider.clamp(MIN_DIVIDER, MAX_DIVIDER) - 1;
+ ral::modify_reg!(ral::ccm, ccm, CSCDR1, UART_CLK_PODF: podf);
+ }
+
+ /// UART clock selection.
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// PLL 3 divided by 6.
+ ///
+ /// This is typically 480MHz / 6 == 80MHz.
+ Pll3Div6 = 0,
+ /// 24MHz oscillator.
+ Oscillator = 1,
+ }
+
+ /// Return the UART clock selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ match ral::read_reg!(ral::ccm, ccm, CSCDR1, UART_CLK_SEL) {
+ 0 => Selection::Pll3Div6,
+ 1 => Selection::Oscillator,
+ _ => unreachable!(),
+ }
+ }
+
+ /// Set the UART clock selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CSCDR1, UART_CLK_SEL: selection as u32);
+ }
+}
+
+/// LPI2C clock root.
+///
+/// `lpi2c_clk` provides the clock source for all LPI2C peripherals.
+/// You must disable LPI2C clock gates before selecting the clock
+/// and divider.
+pub mod lpi2c_clk {
+ use crate::ral::{self, ccm::CCM};
+
+ /// Returns the LPI2C clock divider.
+ pub fn divider(ccm: CCM) -> u32 {
+ ral::read_reg!(ral::ccm, ccm, CSCDR2, LPI2C_CLK_PODF) + 1
+ }
+
+ /// The smallest LPI2C clock divider.
+ pub const MIN_DIVIDER: u32 = 1;
+ /// The largest LPI2C clock divider.
+ pub const MAX_DIVIDER: u32 = 64;
+
+ /// Set the LPI2C clock divider.
+ ///
+ /// The implementation clamps `divider` between [`MIN_DIVIDER`] and [`MAX_DIVIDER`].
+ pub fn set_divider(ccm: CCM, divider: u32) {
+ let podf = divider.clamp(MIN_DIVIDER, MAX_DIVIDER) - 1;
+ ral::modify_reg!(ral::ccm, ccm, CSCDR2, LPI2C_CLK_PODF: podf);
+ }
+
+ /// LPI2C clock selections.
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// Derive from PLL3 divided by 8.
+ Pll3Div8 = 0,
+ /// Derive from the crystal oscillator.
+ Oscillator = 1,
+ }
+
+ /// Returns the LPI2C clock selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ match ral::read_reg!(ral::ccm, ccm, CSCDR2, LPI2C_CLK_SEL) {
+ 0 => Selection::Pll3Div8,
+ 1 => Selection::Oscillator,
+ _ => unreachable!(),
+ }
+ }
+
+ /// Set the LPI2C clock selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CSCDR2, LPI2C_CLK_SEL: selection as u32);
+ }
+}
+
+/// LPSPI clock root.
+///
+/// `lpspi_clk` provides the clock source for all LPSPI peripherals.
+/// You must disable LPSPI clock gates before selecting the clock
+/// and divider.
+pub mod lpspi_clk {
+ use crate::ral::{self, ccm::CCM};
+
+ /// Returns the LPSPI clock divider.
+ pub fn divider(ccm: CCM) -> u32 {
+ ral::read_reg!(ral::ccm, ccm, CBCMR, LPSPI_PODF) + 1
+ }
+
+ /// The smallest LPSPI clock divider.
+ pub const MIN_DIVIDER: u32 = 1;
+ /// The largest LPSPI clock divider.
+ pub const MAX_DIVIDER: u32 = 8;
+
+ /// Set the LPSPI clock divider.
+ ///
+ /// The implementation clamps `divider` between [`MIN_DIVIDER`] and [`MAX_DIVIDER`].
+ pub fn set_divider(ccm: CCM, divider: u32) {
+ // 1010 MCUs support an extra bit in this field, so this
+ // could be a max of 16 for those chips.
+ let podf = divider.clamp(MIN_DIVIDER, MAX_DIVIDER) - 1;
+ ral::modify_reg!(ral::ccm, ccm, CBCMR, LPSPI_PODF: podf);
+ }
+
+ /// LPSPI clock selections.
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// Derive from PLL3_PFD1.
+ Pll3Pfd1 = 0,
+ /// Derive from the PLL3_PFD0.
+ Pll3Pfd0 = 1,
+ /// Derive from PLL2.
+ Pll2 = 2,
+ /// Derive from PLL2_PFD2.
+ Pll2Pfd2 = 3,
+ }
+
+ /// Returns the LPSPI clock selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ match ral::read_reg!(ral::ccm, ccm, CBCMR, LPSPI_CLK_SEL) {
+ 0 => Selection::Pll3Pfd1,
+ 1 => Selection::Pll3Pfd0,
+ 2 => Selection::Pll2,
+ 3 => Selection::Pll2Pfd2,
+ _ => unreachable!(),
+ }
+ }
+
+ /// Set the LPSPI clock selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CBCMR, LPSPI_CLK_SEL: selection as u32);
+ }
+}
+
+/// AHB (ARM) clock root.
+///
+/// The AHB module exposes all selections and dividers that affect
+/// the AHB / ARM clock. This includes all clock trees behind the main
+/// selector, and the driving PLL.
+///
+/// Chip-specific features affect what's exposed from this module.
+///
+/// Note: the i.MX RT 1010 processors refer to the AHB clock root as the
+/// "core clock root." Nevertheless, the core clock root has an "AHB divider."
+/// For consistency, we refer to the 1010's core clock root as the AHB clock
+/// root.
+pub mod ahb_clk {
+ use crate::ral::{self, ccm::CCM};
+
+ /// Set the AHB divider.
+ ///
+ /// The implementation clamps `divider` between 1 and 8.
+ pub fn set_divider(ccm: CCM, divider: u32) {
+ let podf = divider.clamp(1, 8) - 1;
+ ral::modify_reg!(ral::ccm, ccm, CBCDR, AHB_PODF: podf);
+ super::wait_handshake(ccm);
+ }
+
+ /// Returns the AHB divider.
+ pub fn divider(ccm: CCM) -> u32 {
+ ral::read_reg!(ral::ccm, ccm, CBCDR, AHB_PODF) + 1
+ }
+
+ /// Peripheral clock selection.
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// Derive from PRE_PERIPH_CLK.
+ PrePeriphClkSel = 0,
+ /// Derive from PERIPH_CLK2.
+ PeriphClk2Sel = 1,
+ }
+
+ /// Set the peripheral clock selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CBCDR, PERIPH_CLK_SEL: selection as u32);
+ super::wait_handshake(ccm);
+ }
+
+ /// Returns the peripheral clock selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ match ral::read_reg!(ral::ccm, ccm, CBCDR, PERIPH_CLK_SEL) {
+ 0 => Selection::PrePeriphClkSel,
+ 1 => Selection::PeriphClk2Sel,
+ _ => unreachable!(),
+ }
+ }
+}
+
+/// ARM divider.
+///
+/// This divides the output from the AHB PLL (either PLL1 or PLL6, depending
+/// on the system).
+pub mod arm_divider {
+ use crate::ral::{self, ccm::CCM};
+
+ /// Set the ARM divider.
+ ///
+ /// The implementation clamps `divider` between 1 and 8.
+ pub fn set_divider(ccm: CCM, divider: u32) {
+ let podf = divider.clamp(1, 8) - 1;
+ ral::modify_reg!(ral::ccm, ccm, CACRR, ARM_PODF: podf);
+ crate::ccm::wait_handshake(ccm);
+ }
+
+ /// Returns the ARM divider.
+ pub fn divider(ccm: CCM) -> u32 {
+ ral::read_reg!(ral::ccm, ccm, CACRR, ARM_PODF) + 1
+ }
+}
+
+/// One of the accessory muxes upstream of the AHB / ARM clock.
+pub mod periph_clk2 {
+ use crate::ral::{self, ccm::CCM};
+
+ /// Set the peripheral clock 2 divider.
+ ///
+ /// The implementation clamps `divider` between 1 and 8. You should first switch
+ /// away the core clock selection before changing this divider.
+ pub fn set_divider(ccm: CCM, divider: u32) {
+ let podf = divider.clamp(1, 8) - 1;
+ ral::modify_reg!(ral::ccm, ccm, CBCDR, PERIPH_CLK2_PODF: podf);
+ }
+
+ /// Returns the peripheral clock 2 divider.
+ pub fn divider(ccm: CCM) -> u32 {
+ ral::read_reg!(ral::ccm, ccm, CBCDR, PERIPH_CLK2_PODF) + 1
+ }
+
+ /// Peripheral CLK2 selection.
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// Use PLL3 (possibly bypassed by an upstream mux).
+ Pll3Sw = 0,
+ /// Crystall oscillator.
+ Osc = 1,
+ /// The PLL2 bypass.
+ Pll2Bypass = 2,
+ }
+
+ /// Set the peripheral clock2 selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CBCMR, PERIPH_CLK2_SEL: selection as u32);
+ crate::ccm::wait_handshake(ccm);
+ }
+
+ /// Returns the peripheral clock2 selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ let raw = ral::read_reg!(ral::ccm, ccm, CBCMR, PERIPH_CLK2_SEL);
+ match raw {
+ 0 => Selection::Pll3Sw,
+ 1 => Selection::Osc,
+ 2 => Selection::Pll2Bypass,
+ _ => unreachable!(),
+ }
+ }
+}
+
+/// FlexSPI1 with AXI/SEMC selectors.
+pub mod flexspi1_clk_axi_semc {
+ use crate::{ccm::CCM, ral};
+
+ /// FlexSPI1 selections
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// The SEMC clock root.
+ AxiSemc = 0,
+ /// PLL3.
+ Pll3 = 1,
+ /// PFD2 of PLL2.
+ Pll2Pfd2 = 2,
+ /// PFD0 of PLL3.
+ Pll3Pfd0 = 3,
+ }
+
+ /// Set the FlexSPI1 clock selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CSCMR1, FLEXSPI_CLK_SEL: selection as u32);
+ }
+
+ /// Return the FlexSPI1 clock selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ match ral::read_reg!(ral::ccm, ccm, CSCMR1, FLEXSPI_CLK_SEL) {
+ 0 => Selection::AxiSemc,
+ 1 => Selection::Pll3,
+ 2 => Selection::Pll2Pfd2,
+ 3 => Selection::Pll3Pfd0,
+ _ => unreachable!(),
+ }
+ }
+
+ /// The smallest divider clamped by the implementation.
+ pub const MIN_DIVIDER: u32 = 1;
+
+ /// The largest divider clamped by the implementation.
+ pub const MAX_DIVIDER: u32 = 8;
+
+ /// Return the divider.
+ pub fn divider(ccm: CCM) -> u32 {
+ 1 + ral::read_reg!(ral::ccm, ccm, CSCMR1, FLEXSPI_PODF)
+ }
+
+ /// Set the FlexSPI1 root clock divider.
+ ///
+ /// The implementation clamps the input between [`MIN_DIVIDER`]
+ /// and [`MAX_DIVIDER`].
+ pub fn set_divider(ccm: CCM, divider: u32) {
+ let divider = divider.clamp(MIN_DIVIDER, MAX_DIVIDER) - 1;
+ ral::modify_reg!(ral::ccm, ccm, CSCMR1, FLEXSPI_PODF: divider);
+ }
+}
+
+/// FlexSPI1 with a PLL2 root clock.
+///
+/// This root clock can also switch to the `periph_clk2` source.
+/// However, this isn't yet implemented.
+pub mod flexspi1_clk_root_pll2 {
+ #[doc(inline)]
+ pub use super::flexspi1_clk_axi_semc::{MAX_DIVIDER, MIN_DIVIDER, divider, set_divider};
+ use crate::{ccm::CCM, ral};
+
+ /// FlexSPI1 selections
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// PLL2.
+ Pll2 = 0,
+ /// PLL3.
+ Pll3 = 1,
+ /// PFD2 of PLL2.
+ Pll2Pfd2 = 2,
+ /// PFD0 of PLL3.
+ Pll3Pfd0 = 3,
+ }
+
+ /// Set the FlexSPI1 clock selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CSCMR1, FLEXSPI_CLK_SEL: selection as u32);
+ }
+
+ /// Return the FlexSPI1 clock selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ match ral::read_reg!(ral::ccm, ccm, CSCMR1, FLEXSPI_CLK_SEL) {
+ 0 => Selection::Pll2,
+ 1 => Selection::Pll3,
+ 2 => Selection::Pll2Pfd2,
+ 3 => Selection::Pll3Pfd0,
+ _ => unreachable!(),
+ }
+ }
+}
+
+/// Pre-peripheral clock.
+pub mod pre_periph_clk_pll1 {
+ use crate::ral::{self, ccm::CCM};
+
+ /// Pre-peripheral clock selection.
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// PLL2.
+ Pll2 = 0,
+ /// PFD2 of PLL2
+ Pll2Pfd2 = 1,
+ /// PFD0 of PLL2.
+ Pll2Pfd0 = 2,
+ /// PLL1.
+ Pll1 = 3,
+ }
+
+ /// Set the pre-peripheral clock selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CBCMR, PRE_PERIPH_CLK_SEL: selection as u32);
+ }
+
+ /// Returns the pre-peripheral clock selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ use Selection::*;
+ match ral::read_reg!(ral::ccm, ccm, CBCMR, PRE_PERIPH_CLK_SEL) {
+ 0 => Pll2,
+ 1 => Pll2Pfd2,
+ 2 => Pll2Pfd0,
+ 3 => Pll1,
+ _ => unreachable!(),
+ }
+ }
+}
+
+/// Pre-peripheral clock.
+pub mod pre_periph_clk_pll6 {
+ use crate::ral::{self, ccm::CCM};
+
+ /// Pre-peripheral clock selection.
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// PLL2.
+ Pll2 = 0,
+ /// PFD3 of PLL3.
+ Pll3Pfd3 = 1,
+ /// PFD3 of PLL2.
+ Pll2Pfd3 = 2,
+ /// PLL6.
+ Pll6 = 3,
+ }
+
+ /// Set the pre-peripheral clock selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CBCMR, PRE_PERIPH_CLK_SEL: selection as u32);
+ }
+
+ /// Returns the pre-peripheral clock selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ use Selection::*;
+ match ral::read_reg!(ral::ccm, ccm, CBCMR, PRE_PERIPH_CLK_SEL) {
+ 0 => Pll2,
+ 1 => Pll3Pfd3,
+ 2 => Pll2Pfd3,
+ 3 => Pll6,
+ _ => unreachable!(),
+ }
+ }
+}
+
+/// Clock gate control.
+pub mod clock_gate {
+ use crate::ral::{self, ccm::CCM};
+
+ /// A clock gate locator.
+ ///
+ /// This enum encodes the register and field for a clock gate.
+ /// Note that not all clock gates are valid for all MCUs.
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u16)]
+ pub enum Locator {
+ Ccgr0Cg00 = clock_gate(0, 00),
+ Ccgr0Cg01 = clock_gate(0, 01),
+ Ccgr0Cg02 = clock_gate(0, 02),
+ Ccgr0Cg03 = clock_gate(0, 03),
+ Ccgr0Cg04 = clock_gate(0, 04),
+ Ccgr0Cg05 = clock_gate(0, 05),
+ Ccgr0Cg06 = clock_gate(0, 06),
+ Ccgr0Cg07 = clock_gate(0, 07),
+ Ccgr0Cg08 = clock_gate(0, 08),
+ Ccgr0Cg09 = clock_gate(0, 09),
+ Ccgr0Cg10 = clock_gate(0, 10),
+ Ccgr0Cg11 = clock_gate(0, 11),
+ Ccgr0Cg12 = clock_gate(0, 12),
+ Ccgr0Cg13 = clock_gate(0, 13),
+ Ccgr0Cg14 = clock_gate(0, 14),
+ Ccgr0Cg15 = clock_gate(0, 15),
+
+ Ccgr1Cg00 = clock_gate(1, 00),
+ Ccgr1Cg01 = clock_gate(1, 01),
+ Ccgr1Cg02 = clock_gate(1, 02),
+ Ccgr1Cg03 = clock_gate(1, 03),
+ Ccgr1Cg04 = clock_gate(1, 04),
+ Ccgr1Cg05 = clock_gate(1, 05),
+ Ccgr1Cg06 = clock_gate(1, 06),
+ Ccgr1Cg07 = clock_gate(1, 07),
+ Ccgr1Cg08 = clock_gate(1, 08),
+ Ccgr1Cg09 = clock_gate(1, 09),
+ Ccgr1Cg10 = clock_gate(1, 10),
+ Ccgr1Cg11 = clock_gate(1, 11),
+ Ccgr1Cg12 = clock_gate(1, 12),
+ Ccgr1Cg13 = clock_gate(1, 13),
+ Ccgr1Cg14 = clock_gate(1, 14),
+ Ccgr1Cg15 = clock_gate(1, 15),
+
+ Ccgr2Cg00 = clock_gate(2, 00),
+ Ccgr2Cg01 = clock_gate(2, 01),
+ Ccgr2Cg02 = clock_gate(2, 02),
+ Ccgr2Cg03 = clock_gate(2, 03),
+ Ccgr2Cg04 = clock_gate(2, 04),
+ Ccgr2Cg05 = clock_gate(2, 05),
+ Ccgr2Cg06 = clock_gate(2, 06),
+ Ccgr2Cg07 = clock_gate(2, 07),
+ Ccgr2Cg08 = clock_gate(2, 08),
+ Ccgr2Cg09 = clock_gate(2, 09),
+ Ccgr2Cg10 = clock_gate(2, 10),
+ Ccgr2Cg11 = clock_gate(2, 11),
+ Ccgr2Cg12 = clock_gate(2, 12),
+ Ccgr2Cg13 = clock_gate(2, 13),
+ Ccgr2Cg14 = clock_gate(2, 14),
+ Ccgr2Cg15 = clock_gate(2, 15),
+
+ Ccgr3Cg00 = clock_gate(3, 00),
+ Ccgr3Cg01 = clock_gate(3, 01),
+ Ccgr3Cg02 = clock_gate(3, 02),
+ Ccgr3Cg03 = clock_gate(3, 03),
+ Ccgr3Cg04 = clock_gate(3, 04),
+ Ccgr3Cg05 = clock_gate(3, 05),
+ Ccgr3Cg06 = clock_gate(3, 06),
+ Ccgr3Cg07 = clock_gate(3, 07),
+ Ccgr3Cg08 = clock_gate(3, 08),
+ Ccgr3Cg09 = clock_gate(3, 09),
+ Ccgr3Cg10 = clock_gate(3, 10),
+ Ccgr3Cg11 = clock_gate(3, 11),
+ Ccgr3Cg12 = clock_gate(3, 12),
+ Ccgr3Cg13 = clock_gate(3, 13),
+ Ccgr3Cg14 = clock_gate(3, 14),
+ Ccgr3Cg15 = clock_gate(3, 15),
+
+ Ccgr4Cg00 = clock_gate(4, 00),
+ Ccgr4Cg01 = clock_gate(4, 01),
+ Ccgr4Cg02 = clock_gate(4, 02),
+ Ccgr4Cg03 = clock_gate(4, 03),
+ Ccgr4Cg04 = clock_gate(4, 04),
+ Ccgr4Cg05 = clock_gate(4, 05),
+ Ccgr4Cg06 = clock_gate(4, 06),
+ Ccgr4Cg07 = clock_gate(4, 07),
+ Ccgr4Cg08 = clock_gate(4, 08),
+ Ccgr4Cg09 = clock_gate(4, 09),
+ Ccgr4Cg10 = clock_gate(4, 10),
+ Ccgr4Cg11 = clock_gate(4, 11),
+ Ccgr4Cg12 = clock_gate(4, 12),
+ Ccgr4Cg13 = clock_gate(4, 13),
+ Ccgr4Cg14 = clock_gate(4, 14),
+ Ccgr4Cg15 = clock_gate(4, 15),
+
+ Ccgr5Cg00 = clock_gate(5, 00),
+ Ccgr5Cg01 = clock_gate(5, 01),
+ Ccgr5Cg02 = clock_gate(5, 02),
+ Ccgr5Cg03 = clock_gate(5, 03),
+ Ccgr5Cg04 = clock_gate(5, 04),
+ Ccgr5Cg05 = clock_gate(5, 05),
+ Ccgr5Cg06 = clock_gate(5, 06),
+ Ccgr5Cg07 = clock_gate(5, 07),
+ Ccgr5Cg08 = clock_gate(5, 08),
+ Ccgr5Cg09 = clock_gate(5, 09),
+ Ccgr5Cg10 = clock_gate(5, 10),
+ Ccgr5Cg11 = clock_gate(5, 11),
+ Ccgr5Cg12 = clock_gate(5, 12),
+ Ccgr5Cg13 = clock_gate(5, 13),
+ Ccgr5Cg14 = clock_gate(5, 14),
+ Ccgr5Cg15 = clock_gate(5, 15),
+
+ Ccgr6Cg00 = clock_gate(6, 00),
+ Ccgr6Cg01 = clock_gate(6, 01),
+ Ccgr6Cg02 = clock_gate(6, 02),
+ Ccgr6Cg03 = clock_gate(6, 03),
+ Ccgr6Cg04 = clock_gate(6, 04),
+ Ccgr6Cg05 = clock_gate(6, 05),
+ Ccgr6Cg06 = clock_gate(6, 06),
+ Ccgr6Cg07 = clock_gate(6, 07),
+ Ccgr6Cg08 = clock_gate(6, 08),
+ Ccgr6Cg09 = clock_gate(6, 09),
+ Ccgr6Cg10 = clock_gate(6, 10),
+ Ccgr6Cg11 = clock_gate(6, 11),
+ Ccgr6Cg12 = clock_gate(6, 12),
+ Ccgr6Cg13 = clock_gate(6, 13),
+ Ccgr6Cg14 = clock_gate(6, 14),
+ Ccgr6Cg15 = clock_gate(6, 15),
+
+ Ccgr7Cg00 = clock_gate(7, 00),
+ Ccgr7Cg01 = clock_gate(7, 01),
+ Ccgr7Cg02 = clock_gate(7, 02),
+ Ccgr7Cg03 = clock_gate(7, 03),
+ Ccgr7Cg04 = clock_gate(7, 04),
+ Ccgr7Cg05 = clock_gate(7, 05),
+ Ccgr7Cg06 = clock_gate(7, 06),
+ Ccgr7Cg07 = clock_gate(7, 07),
+ Ccgr7Cg08 = clock_gate(7, 08),
+ Ccgr7Cg09 = clock_gate(7, 09),
+ Ccgr7Cg10 = clock_gate(7, 10),
+ Ccgr7Cg11 = clock_gate(7, 11),
+ Ccgr7Cg12 = clock_gate(7, 12),
+ Ccgr7Cg13 = clock_gate(7, 13),
+ Ccgr7Cg14 = clock_gate(7, 14),
+ Ccgr7Cg15 = clock_gate(7, 15),
+ }
+
+ /// Encode a clock gate.
+ const fn clock_gate(register: u8, field: u8) -> u16 {
+ ((register as u16) << 8) | ((field * 2) as u16)
+ }
+
+ impl Locator {
+ /// Access the register index.
+ const fn register(self) -> usize {
+ (((self as u16) >> 8) & 0xFF) as usize
+ }
+ /// Produce the bit shift for the field.
+ const fn shift(self) -> u32 {
+ ((self as u16) & 0xFF) as u32
+ }
+ /// Produce the mask for the field.
+ const fn mask(self) -> u32 {
+ 0b11 << self.shift()
+ }
+ }
+
+ /// The activity for a [`Locator`].
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Activity {
+ /// Clock is off in all modes.
+ Off = 0,
+ /// Clock is on in run mode, but off in WAIT and STOP modes.
+ OnlyRun = 1,
+ /// Clock is on in all modes, except stop mode.
+ On = 3,
+ }
+
+ impl From<bool> for Activity {
+ fn from(value: bool) -> Self {
+ if value { Self::On } else { Self::Off }
+ }
+ }
+
+ /// Helper constant to turn off a clock gate.
+ pub const OFF: Activity = Activity::Off;
+ /// Helper constant to turn on a clock gate.
+ pub const ON: Activity = Activity::On;
+
+ /// Returns a clock gate's activity.
+ ///
+ /// Returns `None` if the field represents the reserved value.
+ pub fn get(ccm: CCM, locator: Locator) -> Option<Activity> {
+ let ccgr = ral::read_reg!(ral::ccm, ccm, CCGR[locator.register()]);
+ let field = (ccgr & locator.mask()) >> locator.shift();
+ Some(match field {
+ 0 => Activity::Off,
+ 1 => Activity::OnlyRun,
+ 3 => Activity::On,
+ _ => return None,
+ })
+ }
+
+ /// Set the clock gate's activity.
+ pub fn set(ccm: CCM, locator: Locator, activity: Activity) {
+ ral::modify_reg!(ral::ccm, ccm, CCGR[locator.register()], |mut ccgr| {
+ ccgr &= !locator.mask();
+ ccgr |= (activity as u32) << locator.shift();
+ ccgr
+ });
+ }
+}
diff --git a/drivers/ccm-10xx/src/ccm_analog.rs b/drivers/ccm-10xx/src/ccm_analog.rs
new file mode 100644
index 0000000..a427827
--- /dev/null
+++ b/drivers/ccm-10xx/src/ccm_analog.rs
@@ -0,0 +1,244 @@
+use core::num::NonZero;
+
+pub use crate::ral::ccm_analog::CCM_ANALOG;
+pub type Instance = ral_registers::Instance<crate::ral::ccm_analog::RegisterBlock>;
+
+/// Frequency (Hz) of the external crystal oscillator.
+pub const XTAL_OSCILLATOR_HZ: u32 = 24_000_000;
+
+const fn pll_pfd_divider(pll_hz: u32, target_hz: u32) -> Option<NonZero<u32>> {
+ let div = pll_hz / target_hz * 18;
+ // Safety: divider is between the non-zero min and max values.
+ unsafe {
+ if pll3::MIN_FRAC <= div && div <= pll3::MAX_FRAC {
+ Some(NonZero::new_unchecked(div))
+ } else {
+ None
+ }
+ }
+}
+
+/// The system PLL.
+pub mod pll2 {
+ use core::num::NonZero;
+
+ use crate::ral;
+
+ /// PLL2 frequency (Hz).
+ ///
+ /// The reference manual notes that PLL2 should always run at 528MHz,
+ /// so this constant assumes that PLL2's DIV_SELECT field isn't
+ /// changed at runtime.
+ pub const FREQUENCY: u32 = 528_000_000;
+
+ /// The smallest PLL2_PFD divider.
+ pub const MIN_FRAC: u32 = super::pll3::MIN_FRAC;
+ /// The largest PLL2_PFD divider.
+ pub const MAX_FRAC: u32 = super::pll3::MAX_FRAC;
+
+ /// Produces a PFD divider to reach the target PFD frequency, in Hz.
+ pub const fn pll_pfd_divider(target_hz: u32) -> Option<NonZero<u32>> {
+ super::pll_pfd_divider(FREQUENCY, target_hz)
+ }
+
+ /// Restart the system PLL.
+ pub fn restart(ccm_analog: super::CCM_ANALOG) {
+ loop {
+ if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_SYS, ENABLE == 0) {
+ ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_SYS_SET, ENABLE: 1);
+ continue;
+ }
+ if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_SYS, POWERDOWN == 1) {
+ ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_SYS_CLR, POWERDOWN: 1);
+ continue;
+ }
+ if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_SYS, LOCK == 0) {
+ continue;
+ }
+ if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_SYS, BYPASS == 1) {
+ ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_SYS_CLR, BYPASS: 1);
+ continue;
+ }
+ break;
+ }
+ }
+}
+
+/// The USB PLL.
+///
+/// When an implementation has multiple USB peripherals, this
+/// PLL is associated with USB1.
+pub mod pll3 {
+ /// PLL3 frequency (Hz).
+ ///
+ /// The reference manual notes that PLL3 should always run at 480MHz,
+ /// so this constant assumes that PLL3's DIV_SELECT field isn't
+ /// changed at runtime.
+ pub const FREQUENCY: u32 = 480_000_000;
+
+ /// The smallest PLL3_PFD divider.
+ pub const MIN_FRAC: u32 = 12;
+ /// The largest PLL3_PFD divider.
+ pub const MAX_FRAC: u32 = 35;
+
+ use core::num::NonZero;
+
+ use crate::ral;
+
+ /// Produces a PFD divider to reach the target PFD frequency, in Hz.
+ pub const fn pll_pfd_divider(target_hz: u32) -> Option<NonZero<u32>> {
+ super::pll_pfd_divider(FREQUENCY, target_hz)
+ }
+
+ /// Restart the USB(1) PLL.
+ pub fn restart(ccm_analog: super::CCM_ANALOG) {
+ loop {
+ if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB1, ENABLE == 0) {
+ ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB1_SET, ENABLE: 1);
+ continue;
+ }
+ if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB1, POWER == 0) {
+ ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB1_SET, POWER: 1);
+ continue;
+ }
+ if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB1, LOCK == 0) {
+ continue;
+ }
+ if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB1, BYPASS == 1) {
+ ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB1_CLR, BYPASS: 1);
+ continue;
+ }
+ if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB1, EN_USB_CLKS == 0) {
+ ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB1_SET, EN_USB_CLKS: 1);
+ continue;
+ }
+ break;
+ }
+ }
+}
+
+/// The USB2 PLL, when available.
+pub mod pll7 {
+ /// PLL7 frequency (Hz).
+ ///
+ /// The reference manual notes that PLL7 should always run at 480MHz,
+ /// so this constant assumes that PLL7's DIV_SELECT field isn't
+ /// changed at runtime.
+ pub const FREQUENCY: u32 = 480_000_000;
+
+ /// The smallest PLL7_PFD divider.
+ pub const MIN_FRAC: u8 = 12;
+ /// The largest PLL7_PFD divider.
+ pub const MAX_FRAC: u8 = 35;
+
+ use crate::ral;
+
+ /// Restart the USB2 PLL.
+ pub fn restart(ccm_analog: super::CCM_ANALOG) {
+ loop {
+ if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB2, ENABLE == 0) {
+ ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB2_SET, ENABLE: 1);
+ continue;
+ }
+ if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB2, POWER == 0) {
+ ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB2_SET, POWER: 1);
+ continue;
+ }
+ if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB2, LOCK == 0) {
+ continue;
+ }
+ if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB2, BYPASS == 1) {
+ ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB2_CLR, BYPASS: 1);
+ continue;
+ }
+ if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB2, EN_USB_CLKS == 0) {
+ ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB2_SET, EN_USB_CLKS: 1);
+ continue;
+ }
+ break;
+ }
+ }
+}
+
+/// The ARM PLL.
+///
+/// When available, this is always divided by the CCM `ARM_DIVIDER`.
+pub mod pll1 {
+ use crate::ral;
+
+ /// Restart PLL1 with a new divider selection.
+ ///
+ /// PLL1 should not be driving any components when
+ /// this restart happens. You're responsible for
+ /// switching over clocks.
+ ///
+ /// The implementation clamps `div_sel` between 54 and 108.
+ ///
+ /// When this function returns, PLL1 is running and stable.
+ pub fn restart(ccm_analog: super::CCM_ANALOG, div_sel: u32) {
+ // Restart PLL1.
+ ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_ARM, POWERDOWN: 1);
+ // Clears POWERDOWN bit from above.
+ ral::write_reg!(
+ ral::ccm_analog,
+ ccm_analog,
+ PLL_ARM,
+ DIV_SELECT: div_sel.clamp(54, 108)
+ );
+ ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_ARM_SET, ENABLE: 1);
+ // Wait for lock...
+ while ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_ARM, LOCK == 0) {}
+ }
+
+ /// Compute the PLL1 frequency (Hz) for a `DIV_SEL` value.
+ pub const fn frequency(div_sel: u32) -> u32 {
+ super::XTAL_OSCILLATOR_HZ * div_sel / 2
+ }
+}
+
+/// A fixed 500MHz ENET PLL.
+pub mod pll6_500mhz {
+ use crate::ral;
+
+ /// PLL6 frequency (Hz).
+ pub const FREQUENCY: u32 = 500_000_000;
+
+ /// Restart PLL6.
+ ///
+ /// PLL6 should not be driving any components
+ /// when this restart happens. You're responsible
+ /// for switching over clocks.
+ ///
+ /// When this function returns, PLL6 is running and
+ /// stable.
+ pub fn restart(ccm_analog: super::CCM_ANALOG) {
+ // Clears BYPASS, if enabled.
+ ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_ENET, POWERDOWN: 1);
+ // Clears POWERDOWN from above.
+ ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_ENET, ENET_500M_REF_EN: 1);
+ while ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_ENET, LOCK == 0) {}
+ }
+}
+
+/// The configurable ENET PLL.
+pub mod pll6 {
+ use crate::ral::{self, ccm_analog};
+
+ /// Frequency selection.
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Frequency {
+ Frequency25MHz = ccm_analog::PLL_ENET::DIV_SELECT::DIV_25MHZ,
+ Frequency50MHz = ccm_analog::PLL_ENET::DIV_SELECT::DIV_50MHZ,
+ Frequency100MHz = ccm_analog::PLL_ENET::DIV_SELECT::DIV_100MHZ,
+ Frequency125MHz = ccm_analog::PLL_ENET::DIV_SELECT::DIV_125MHZ,
+ }
+
+ /// Restart the PLL with a new frequency.
+ pub fn restart(ccm_analog: ccm_analog::CCM_ANALOG, frequency: Frequency) {
+ ral::modify_reg!(ccm_analog, ccm_analog, PLL_ENET, BYPASS: 1, BYPASS_CLK_SRC: 0);
+ ral::modify_reg!(ccm_analog, ccm_analog, PLL_ENET, ENABLE: 1, POWERDOWN: 0, DIV_SELECT: frequency as u32);
+ while ral::read_reg!(ccm_analog, ccm_analog, PLL_ENET, LOCK == 0) {}
+ ral::modify_reg!(ccm_analog, ccm_analog, PLL_ENET, BYPASS: 0);
+ }
+}
diff --git a/drivers/ccm-10xx/src/lib.rs b/drivers/ccm-10xx/src/lib.rs
new file mode 100644
index 0000000..ad286cf
--- /dev/null
+++ b/drivers/ccm-10xx/src/lib.rs
@@ -0,0 +1,18 @@
+//! A clock configuration module (CCM) driver for i.MX RT 1000 MCUs.
+//!
+//! The APIs and modules exposed by this package may not always work on your
+//! MCU! You're expected to pick and choose the components that are available.
+//! If you're not sure what those are, use a chip-specific package that does
+//! the filtering for you.
+
+#![no_std]
+
+pub mod ral {
+ pub(crate) use ral_registers::{modify_reg, read_reg, write_reg};
+ pub mod ccm;
+ pub mod ccm_analog;
+}
+
+pub mod ahb;
+pub mod ccm;
+pub mod ccm_analog;
diff --git a/drivers/ccm-10xx/src/ral/ccm.rs b/drivers/ccm-10xx/src/ral/ccm.rs
new file mode 100644
index 0000000..e8033ac
--- /dev/null
+++ b/drivers/ccm-10xx/src/ral/ccm.rs
@@ -0,0 +1,85 @@
+use ral_registers::{Instance, register};
+
+#[repr(C)]
+#[allow(non_snake_case)]
+pub struct RegisterBlock {
+ pub CCR: u32,
+ _reserved0: [u8; 0x04],
+ pub CSR: u32,
+ pub CCSR: u32,
+ pub CACRR: u32,
+ pub CBCDR: u32,
+ pub CBCMR: u32,
+ pub CSCMR1: u32,
+ pub CSCMR2: u32,
+ pub CSCDR1: u32,
+ pub CS1CDR: u32,
+ pub CS2CDR: u32,
+ pub CDCDR: u32,
+ _reserved1: [u8; 0x04],
+ pub CSCDR2: u32,
+ pub CSCDR3: u32,
+ _reserved2: [u8; 0x08],
+ pub CDHIPR: u32,
+ _reserved3: [u8; 0x08],
+ pub CLPCR: u32,
+ pub CISR: u32,
+ pub CIMR: u32,
+ pub CCOSR: u32,
+ pub CGPR: u32,
+ pub CCGR: [u32; 8],
+ pub CMEOR: u32,
+}
+
+/// A CCM instance.
+pub type CCM = Instance<RegisterBlock>;
+
+register!(pub(crate) CACRR<u32> RW [
+ ARM_PODF start(0) width(3) RW {}
+]);
+
+register!(pub(crate) CSCMR1<u32> RW [
+ FLEXSPI_CLK_SRC start(31) width(1) RW {}
+ FLEXSPI_CLK_SEL start(29) width(2) RW {}
+ FLEXSPI_PODF start(23) width(3) RW {}
+ PERCLK_PODF start(0) width(6) RW {}
+ PERCLK_CLK_SEL start(6) width(1) RW {}
+]);
+
+register!(pub(crate) CBCDR<u32> RW [
+ PERIPH_CLK2_PODF start(27) width(3) RW {}
+ PERIPH_CLK_SEL start(25) width(1) RW {}
+ AHB_PODF start(10) width(3) RW {}
+ IPG_PODF start(8) width(2) RW {}
+]);
+
+register!(pub(crate) CLPCR<u32> RW [
+ LPM start(0) width(2) RW {
+ RUN = 0,
+ WAIT = 1,
+ STOP = 2,
+ }
+]);
+
+register!(pub(crate) CSCDR1<u32> RW [
+ UART_CLK_PODF start(0) width(6) RW {}
+ /// Reduced to 1 bit, which is supported across
+ /// all chip variants.
+ UART_CLK_SEL start(6) width(1) RW {}
+]);
+
+register!(pub(crate) CSCDR2<u32> RW [
+ LPI2C_CLK_PODF start(19) width(6) RW {}
+ LPI2C_CLK_SEL start(18) width(1) RW {}
+]);
+
+register!(pub(crate) CBCMR<u32> RW [
+ /// Four bits wide on the 1010.
+ LPSPI_PODF start(26) width(3) RW {}
+ PRE_PERIPH_CLK_SEL start(18) width(2) RW {}
+ PERIPH_CLK2_SEL start(12) width(2) RW {}
+ LPSPI_CLK_SEL start(4) width(2) RW {}
+]);
+
+register!(pub(crate) CCGR<u32> RW []);
+register!(pub(crate) CDHIPR<u32> RO []);
diff --git a/drivers/ccm-10xx/src/ral/ccm_analog.rs b/drivers/ccm-10xx/src/ral/ccm_analog.rs
new file mode 100644
index 0000000..958c7c1
--- /dev/null
+++ b/drivers/ccm-10xx/src/ral/ccm_analog.rs
@@ -0,0 +1,129 @@
+use ral_registers::{Instance, register};
+
+#[repr(C)]
+#[allow(non_snake_case)]
+pub struct RegisterBlock {
+ pub PLL_ARM: u32,
+ pub PLL_ARM_SET: u32,
+ pub PLL_ARM_CLR: u32,
+ pub PLL_ARM_TOG: u32,
+ pub PLL_USB1: u32,
+ pub PLL_USB1_SET: u32,
+ pub PLL_USB1_CLR: u32,
+ pub PLL_USB1_TOG: u32,
+ pub PLL_USB2: u32,
+ pub PLL_USB2_SET: u32,
+ pub PLL_USB2_CLR: u32,
+ pub PLL_USB2_TOG: u32,
+ pub PLL_SYS: u32,
+ pub PLL_SYS_SET: u32,
+ pub PLL_SYS_CLR: u32,
+ pub PLL_SYS_TOG: u32,
+ pub PLL_SYS_SS: u32,
+ _reserved0: [u8; 12],
+ pub PLL_SYS_NUM: u32,
+ _reserved1: [u8; 12],
+ pub PLL_SYS_DENOM: u32,
+ _reserved2: [u8; 12],
+ pub PLL_AUDIO: u32,
+ pub PLL_AUDIO_SET: u32,
+ pub PLL_AUDIO_CLR: u32,
+ pub PLL_AUDIO_TOG: u32,
+ pub PLL_AUDIO_NUM: u32,
+ _reserved3: [u8; 12],
+ pub PLL_AUDIO_DENOM: u32,
+ _reserved4: [u8; 12],
+ pub PLL_VIDEO: u32,
+ pub PLL_VIDEO_SET: u32,
+ pub PLL_VIDEO_CLR: u32,
+ pub PLL_VIDEO_TOG: u32,
+ pub PLL_VIDEO_NUM: u32,
+ _reserved5: [u8; 12],
+ pub PLL_VIDEO_DENOM: u32,
+ _reserved6: [u8; 28],
+ pub PLL_ENET: u32,
+ pub PLL_ENET_SET: u32,
+ pub PLL_ENET_CLR: u32,
+ pub PLL_ENET_TOG: u32,
+ pub PFD_480: u32,
+ pub PFD_480_SET: u32,
+ pub PFD_480_CLR: u32,
+ pub PFD_480_TOG: u32,
+ pub PFD_528: u32,
+ pub PFD_528_SET: u32,
+ pub PFD_528_CLR: u32,
+ pub PFD_528_TOG: u32,
+ _reserved7: [u8; 64],
+ pub MISC0: u32,
+ pub MISC0_SET: u32,
+ pub MISC0_CLR: u32,
+ pub MISC0_TOG: u32,
+ pub MISC1: u32,
+ pub MISC1_SET: u32,
+ pub MISC1_CLR: u32,
+ pub MISC1_TOG: u32,
+ pub MISC2: u32,
+ pub MISC2_SET: u32,
+ pub MISC2_CLR: u32,
+ pub MISC2_TOG: u32,
+}
+
+/// A CCM\_ANALOG instance.
+#[allow(non_camel_case_types)]
+pub type CCM_ANALOG = Instance<RegisterBlock>;
+
+register!(pub PLL_USB1<u32> RW [
+ LOCK start(31) width(1) RW {}
+ BYPASS start(16) width(1) RW {}
+ ENABLE start(13) width(1) RW {}
+ POWER start(12) width(1) RW {}
+ EN_USB_CLKS start(6) width(1) RW {}
+]);
+pub use PLL_USB1 as PLL_USB1_SET;
+pub use PLL_USB1 as PLL_USB1_CLR;
+
+pub use PLL_USB1 as PLL_USB2;
+pub use PLL_USB2 as PLL_USB2_SET;
+pub use PLL_USB2 as PLL_USB2_CLR;
+
+register!(pub PLL_ARM<u32> RW [
+ LOCK start(31) width(1) RW {}
+ ENABLE start(13) width(1) RW {}
+ POWERDOWN start(12) width(1) RW {}
+ DIV_SELECT start(0) width(7) RW {}
+]);
+pub use PLL_ARM as PLL_ARM_SET;
+
+register!(pub PLL_ENET<u32> RW [
+ LOCK start(31) width(1) RW {}
+ ENET_500M_REF_EN start(22) width(1) RW {}
+ POWERDOWN start(12) width(1) RW {}
+ BYPASS start(16) width(1) RW {}
+ BYPASS_CLK_SRC start(14) width(2) RW {}
+ ENABLE start(13) width(1) RW {}
+ DIV_SELECT start(0) width(2) RW {
+ DIV_25MHZ = 0,
+ DIV_50MHZ = 1,
+ DIV_100MHZ = 2,
+ DIV_125MHZ = 3,
+ }
+]);
+
+register!(pub PLL_SYS<u32> RW [
+ LOCK start(31) width(1) RW {}
+ BYPASS start(16) width(1) RW {}
+ ENABLE start(13) width(1) RW {}
+ POWERDOWN start(12) width(1) RW {}
+]);
+
+pub use PLL_SYS as PLL_SYS_SET;
+pub use PLL_SYS as PLL_SYS_CLR;
+
+register!(pub PFD_480<u32> RW [
+ PFD3_FRAC start(24) width(6) RW {}
+ PFD2_FRAC start(16) width(6) RW {}
+ PFD1_FRAC start(8) width(6) RW {}
+ PFD0_FRAC start(0) width(6) RW {}
+]);
+
+pub use PFD_480 as PFD_528;
diff --git a/drivers/ccm-11xx/Cargo.toml b/drivers/ccm-11xx/Cargo.toml
new file mode 100644
index 0000000..ed0ffa7
--- /dev/null
+++ b/drivers/ccm-11xx/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "imxrt-drivers-ccm-11xx"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+ral-registers = { workspace = true }
diff --git a/drivers/ccm-11xx/src/lib.rs b/drivers/ccm-11xx/src/lib.rs
new file mode 100644
index 0000000..09982d8
--- /dev/null
+++ b/drivers/ccm-11xx/src/lib.rs
@@ -0,0 +1,465 @@
+#![no_std]
+
+mod ral_common {
+ pub mod gpr_shared {
+ #[repr(C)]
+ #[allow(non_snake_case)]
+ pub struct RegisterBlock {
+ pub REG: u32,
+ pub SET: u32,
+ pub CLR: u32,
+ pub TOG: u32,
+ pub AUTHEN: u32,
+ pub AUTHEN_SET: u32,
+ pub AUTHEN_CLR: u32,
+ pub AUTHEN_TOG: u32,
+ }
+ }
+ pub mod gpr_private {
+ pub use super::gpr_shared::RegisterBlock;
+ }
+}
+
+pub mod ral_11xx {
+ pub mod clock_group;
+ pub mod clock_root;
+ pub mod lpcg;
+ pub mod osc_pll;
+ pub mod pll;
+ use core::num::{NonZeroU8, NonZeroU32};
+
+ pub use crate::ral_common::{gpr_private, gpr_shared};
+
+ pub use self::{
+ clock_group as CLOCK_GROUP, clock_root as CLOCK_ROOT, gpr_private as GPR_PRIVATE,
+ gpr_shared as GPR_SHARED, lpcg as LPCG, osc_pll as OSC_PLL,
+ };
+
+ #[repr(C)]
+ #[allow(non_snake_case)]
+ pub struct RegisterBlock {
+ pub CLOCK_ROOT: [clock_root::RegisterBlock; 79],
+ _reserved0: [u8; 6272],
+ pub CLOCK_GROUP: [clock_group::RegisterBlock; 2],
+ _reserved1: [u8; 1792],
+ pub GPR_SHARED: [gpr_shared::RegisterBlock; 8],
+ _reserved2: [u8; 800 - size_of::<gpr_private::RegisterBlock>()],
+ /// First register block is typically reserved. To avoid this
+ /// reserved block, make sure your index corresponds to the
+ /// register names.
+ pub GPR_PRIVATE: [gpr_private::RegisterBlock; 8],
+ _reserved3: [u8; 768],
+ pub OSC_PLL: [osc_pll::RegisterBlock; 29],
+ _reserved4: [u8; 3168],
+ pub LPCG: [lpcg::RegisterBlock; 138],
+ }
+
+ pub type Instance = ral_registers::Instance<RegisterBlock>;
+
+ /// A clock source.
+ ///
+ /// These typically map to an `OSCPLL`.
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ #[non_exhaustive]
+ pub enum ClockSource {
+ /// The 16MHz RC oscillator.
+ Rc16M = 0,
+ /// The 48MHz RC oscillator.
+ Rc48M = 1,
+ /// The 48MHz RC oscillator with a fixed divide-by-2.
+ Rc48MDiv2 = 2,
+ /// The 400MHz RC oscillator.
+ Rc400M = 3,
+ /// The external 24MHz crystal oscillator VCO.
+ ///
+ /// This output isn't exposed by the clock tree.
+ /// However, it's exposed via [`XtalClk`].
+ Xtal = 4,
+ /// The exposed 24MHz crystal oscillator.
+ XtalClk = 5,
+ /// ARM PLL VCO output.
+ ///
+ /// Not connected to the clock tree.
+ ArmPll = 6,
+ /// The ARM PLL output in the clock tree.
+ ArmPllClk = 7,
+
+ /// Ths voltage controlled oscillator for PLL2.
+ ///
+ /// This output isn't exposed by the clock tree.
+ /// However, the [`Pll2Clk`] is exposed.
+ Pll2 = 8,
+ /// The PLL2 output into the clock tree.
+ Pll2Clk = 9,
+ /// PFD0 of PLL2.
+ Pll2Pfd0 = 10,
+ /// PFD1 of PLL2.
+ Pll2Pfd1 = 11,
+ /// PFD2 of PLL2.
+ Pll2Pfd2 = 12,
+ /// PFD3 of PLL2.
+ Pll2Pfd3 = 13,
+
+ /// Ths voltage controlled oscillator for PLL3.
+ ///
+ /// This output isn't exposed by the clock tree.
+ /// However, the [`Pll3Clk`] is exposed.
+ Pll3 = 14,
+ /// The PLL3 output into the clock tree.
+ Pll3Clk = 15,
+ /// PLL3 with a fixed divide-by-2.
+ Pll3Div2 = 16,
+ /// PFD0 of PLL3.
+ Pll3Pfd0 = 17,
+ /// PFD1 of PLL3.
+ Pll3Pfd1 = 18,
+ /// PFD2 of PLL3.
+ Pll3Pfd2 = 19,
+ /// PFD3 of PLL3.
+ Pll3Pfd3 = 20,
+
+ /// The voltage controlled oscillator for PLL1.
+ ///
+ /// This output isn't exposed by the clock tree.
+ /// However, the [`Pll1Clk`] is exposed.
+ Pll1 = 21,
+ /// The PLL1 output into the clock tree.
+ Pll1Clk = 22,
+ /// PLL1 with a fixed divide-by-2.
+ Pll1Div2 = 23,
+ /// PLL1 with a fixed divide-by-5.
+ Pll1Div5 = 24,
+ }
+
+ /// A clock root.
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ #[non_exhaustive]
+ pub enum ClockRoot {
+ /// Cortex-M7.
+ M7 = 0,
+ /// `BUS_CLK`.
+ Bus = 2,
+ /// `BUS_LPSR_CLK`.
+ BusLpsr = 3,
+ /// SYSTICK for the Cortex-M7.
+ M7Systick = 8,
+ /// FlexSPI1.
+ Flexspi1 = 20,
+ /// FlexSPI2.
+ Flexspi2 = 21,
+ /// LPSPI1.
+ Lpspi1 = 43,
+ /// ENET.
+ Enet = 51,
+ /// ENET_1G.
+ Enet1G = 52,
+ /// ENET QOS on supported MCUs.
+ EnetQos = 53,
+ /// ENET_25M clock.
+ Enet25M = 54,
+ /// ENET PPT.
+ EnetTimer1 = 55,
+ /// ENET_1G PPT.
+ EnetTimer2 = 56,
+ /// ENET QOS PPT on supported MCUs.
+ EnetTimer3 = 57,
+ }
+
+ /// A clock mux value.
+ ///
+ /// Use [`get`](Self::get) to acquire the raw value that
+ /// can be committed to the hardware.
+ #[derive(Clone, Copy, PartialEq, Eq)]
+ #[repr(transparent)]
+ pub struct ClockMux(NonZeroU32);
+
+ impl ClockMux {
+ const fn new(raw: u32) -> Option<Self> {
+ match NonZeroU32::new(raw.saturating_add(1)) {
+ Some(mux) => Some(ClockMux(mux)),
+ None => None,
+ }
+ }
+
+ /// Returns the raw mux value that's valid for
+ /// the hardware.
+ pub const fn get(self) -> u32 {
+ self.0.get() - 1
+ }
+ }
+
+ /// Produces the MUX selection for this clock root, as long
+ /// as the source can be multiplexed to this root.
+ ///
+ /// Returns `None` if the source isn't available for this
+ /// root. It may also return `None` if the mapping isn't implemented.
+ ///
+ /// The implementation may alias names for convenience.
+ /// For example, `Xtal` and `XtalClk` alias to the same mux
+ /// value, although `Xtal` isn't connected to the clock tree.
+ ///
+ /// The implementation may return roots that are marked "reserved"
+ /// on your chip. For example, some M7 clock sources are reserved on
+ /// the 1160 but implemented on the 1170. This call will return those
+ /// reserved muxes for the 1170, and it's the user's job to know that
+ /// the root is invalid on the 1160.
+ pub const fn try_mux(root: ClockRoot, source: ClockSource) -> Option<ClockMux> {
+ // These sources are always available no matter the
+ // user's selected root.
+ match source {
+ ClockSource::Rc48MDiv2 => return ClockMux::new(0b000),
+ ClockSource::Xtal | ClockSource::XtalClk => return ClockMux::new(0b001),
+ ClockSource::Rc400M => return ClockMux::new(0b010),
+ ClockSource::Rc16M => return ClockMux::new(0b011),
+ _ => {}
+ }
+
+ // The remaining sources are specific to the root.
+ match root {
+ ClockRoot::Enet
+ | ClockRoot::Enet1G
+ | ClockRoot::EnetQos
+ | ClockRoot::Enet25M
+ | ClockRoot::EnetTimer1
+ | ClockRoot::EnetTimer2
+ | ClockRoot::EnetTimer3 => match source {
+ ClockSource::Pll1Div2 => ClockMux::new(0b100),
+ ClockSource::Pll1Div5 => ClockMux::new(0b110),
+ _ => None,
+ },
+ ClockRoot::M7 => match source {
+ ClockSource::ArmPll | ClockSource::ArmPllClk => ClockMux::new(0b100),
+ _ => None,
+ },
+ ClockRoot::Bus => match source {
+ ClockSource::Pll1Div5 => ClockMux::new(0b101),
+ _ => None,
+ },
+ ClockRoot::BusLpsr => match source {
+ ClockSource::Pll3Pfd3 => ClockMux::new(0b100),
+ ClockSource::Pll3 | ClockSource::Pll3Clk => ClockMux::new(0b101),
+ ClockSource::Pll2 | ClockSource::Pll2Clk => ClockMux::new(0b110),
+ ClockSource::Pll1Div5 => ClockMux::new(0b111),
+ _ => None,
+ },
+ ClockRoot::M7Systick => match source {
+ ClockSource::Pll1Div5 => ClockMux::new(0b110),
+ _ => None,
+ },
+ ClockRoot::Lpspi1 => match source {
+ _ => None,
+ },
+ ClockRoot::Flexspi1 | ClockRoot::Flexspi2 => match source {
+ ClockSource::Pll3Pfd0 => ClockMux::new(0b100),
+ ClockSource::Pll2 | ClockSource::Pll2Clk => ClockMux::new(0b101),
+ ClockSource::Pll2Pfd2 => ClockMux::new(0b110),
+ ClockSource::Pll3 | ClockSource::Pll3Clk => ClockMux::new(0b111),
+ _ => None,
+ },
+ }
+ }
+
+ /// Returns the MUX selection for the given clock root for the
+ /// source.
+ ///
+ /// See [`try_mux`] for more information.
+ ///
+ /// # Panics
+ ///
+ /// Panics if the clock root cannot be muxed to the given source.
+ pub const fn mux(root: ClockRoot, source: ClockSource) -> ClockMux {
+ let Some(mux) = try_mux(root, source) else {
+ panic!("Root clock cannot be driven by the source");
+ };
+ mux
+ }
+
+ /// Returns `true` if this clock source supports setpoints.
+ ///
+ /// If the source supports setpoints, it can be controlled by
+ /// the GPC.
+ pub fn source_has_setpoint(ccm: Instance, clock_source: ClockSource) -> bool {
+ ral_registers::read_reg!(
+ self,
+ ccm,
+ OSC_PLL[clock_source as usize].CONFIG,
+ SETPOINT_PRESENT == 1
+ )
+ }
+
+ /// Returns `true` if this clock root supports setpoints.
+ ///
+ /// If the root supports setpoints, it can be controlled by
+ /// the GPC.
+ pub fn root_has_setpoint(ccm: Instance, clock_root: ClockRoot) -> bool {
+ ral_registers::read_reg!(
+ self,
+ ccm,
+ CLOCK_ROOT[clock_root as usize].CONFIG,
+ SETPOINT_PRESENT == 1
+ )
+ }
+
+ /// The clock source is invalid for the given operation.
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(transparent)]
+ pub struct InvalidSourceError(pub ClockSource);
+
+ /// The clock root is invalid for the given operation.
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(transparent)]
+ pub struct InvalidRootError(pub ClockRoot);
+
+ /// Enable setpoint mode for this clock source.
+ ///
+ /// This implicitly disables CPULP and domain modes; the source
+ /// cannot operate in different modes. It also disables the source
+ /// clock in the `DIRECT` register.
+ pub fn enable_source_setpoints(
+ ccm: Instance,
+ clock_source: ClockSource,
+ ) -> Result<(), InvalidSourceError> {
+ if !source_has_setpoint(ccm, clock_source) {
+ return Err(InvalidSourceError(clock_source));
+ }
+
+ set_source_direct(ccm, clock_source, false);
+ ral_registers::modify_reg!(self, ccm, OSC_PLL[clock_source as usize].AUTHEN,
+ CPULPM: 0,
+ SETPOINT_MODE: 1,
+ DOMAIN_MODE: 0,
+ );
+
+ Ok(())
+ }
+
+ /// Enables setpoint mode for this clock root.
+ ///
+ /// This implicitly disables domain mode; the root
+ /// cannot operate in different modes.
+ pub fn enable_root_setpoints(
+ ccm: Instance,
+ clock_root: ClockRoot,
+ ) -> Result<(), InvalidRootError> {
+ if !root_has_setpoint(ccm, clock_root) {
+ return Err(InvalidRootError(clock_root));
+ }
+
+ ral_registers::modify_reg!(self, ccm, CLOCK_ROOT[clock_root as usize].AUTHEN,
+ SETPOINT_MODE: 1,
+ DOMAIN_MODE: 0,
+ );
+
+ Ok(())
+ }
+
+ /// Specify which setpoints enable this clock source.
+ ///
+ /// `setpoints` describes the bitmask of enabled setpoints.
+ /// `standby` describes if the source remains enabled during a standby
+ /// transition in that setpoint.
+ pub fn set_source_setpoints(
+ ccm: Instance,
+ clock_source: ClockSource,
+ setpoints: u16,
+ standby: u16,
+ ) {
+ ral_registers::write_reg!(self, ccm, OSC_PLL[clock_source as usize].SETPOINT, SETPOINT: setpoints as u32, STANDBY: standby as u32);
+ }
+
+ /// Set the clock root's selection and divider.
+ ///
+ /// Spins while the change is in progress.
+ pub fn set_clock_root(ccm: Instance, clock_root: ClockRoot, mux: ClockMux, div: NonZeroU8) {
+ ral_registers::modify_reg!(self, ccm, CLOCK_ROOT[clock_root as usize].CONTROL, MUX: mux.get(), DIV: (div.get() as u32) - 1);
+ while ral_registers::read_reg!(
+ self,
+ ccm,
+ CLOCK_ROOT[clock_root as usize].STATUS0,
+ CHANGING == 1
+ ) {}
+ }
+
+ /// Directly set the clock source on or off.
+ pub fn set_source_direct(ccm: Instance, clock_source: ClockSource, on: bool) {
+ ral_registers::write_reg!(self, ccm, OSC_PLL[clock_source as usize].DIRECT, on as u32);
+ }
+}
+
+pub mod ral_1180 {
+ pub use crate::ral_common::{gpr_private, gpr_shared};
+ pub mod clock_root;
+ pub mod lpcg;
+ pub mod observe;
+ pub mod osc_pll;
+
+ pub use self::{
+ clock_root as CLOCK_ROOT, gpr_private as GPR_PRIVATE, gpr_shared as GPR_SHARED,
+ lpcg as LPCG, observe as OBSERVE, osc_pll as OSC_PLL,
+ };
+
+ #[repr(C)]
+ #[allow(non_snake_case)]
+ pub struct RegisterBlock {
+ pub CLOCK_ROOT: [clock_root::RegisterBlock; 74],
+ _reserved0: [u8; 7936],
+ pub OBSERVE: [observe::RegisterBlock; 2],
+ _reserved1: [u8; 768],
+ pub GPR_SHARED: [gpr_shared::RegisterBlock; 16],
+ pub GPR_SHARED_STATUS: [u32; 8],
+ _reserved2: [u8; 480],
+ pub GPR_PRIVATE: [gpr_private::RegisterBlock; 4],
+ _reserved3: [u8; 896],
+ pub OSC_PLL: [osc_pll::RegisterBlock; 25],
+ _reserved4: [u8; 10688],
+ pub LPCG: [lpcg::RegisterBlock; 149],
+ }
+}
+
+ral_registers::register! {
+ #[doc = "Clock root working status"]
+ STATUS0_RO<u32> RO [
+ #[doc = "Current clock root POWERDOWN setting"]
+ POWERDOWN start(27) width(1) RO {}
+ #[doc = "Internal updating in generation logic"]
+ SLICE_BUSY start(28) width(1) RO {}
+ #[doc = "Internal status synchronization to clock generation logic"]
+ UPDATE_FORWARD start(29) width(1) RO {}
+ #[doc = "Internal status synchronization from clock generation logic"]
+ UPDATE_REVERSE start(30) width(1) RO {}
+ #[doc = "Internal updating in clock root"]
+ CHANGING start(31) width(1) RO {}
+ ]
+}
+
+#[cfg(test)]
+mod tests {
+ use core::mem::offset_of;
+
+ #[test]
+ fn layout_1180() {
+ use super::ral_1180::RegisterBlock;
+
+ assert_eq!(offset_of!(RegisterBlock, CLOCK_ROOT), 0);
+ assert_eq!(offset_of!(RegisterBlock, OBSERVE), 0x4400);
+ assert_eq!(offset_of!(RegisterBlock, GPR_SHARED), 0x4800);
+ assert_eq!(offset_of!(RegisterBlock, GPR_SHARED_STATUS), 0x4a00);
+ assert_eq!(offset_of!(RegisterBlock, GPR_PRIVATE), 0x4C00);
+ assert_eq!(offset_of!(RegisterBlock, OSC_PLL), 0x5000,);
+ assert_eq!(offset_of!(RegisterBlock, LPCG), 0x8000);
+ }
+
+ #[test]
+ fn layout_11xx() {
+ use super::ral_11xx::RegisterBlock;
+
+ assert_eq!(offset_of!(RegisterBlock, CLOCK_ROOT), 0);
+ assert_eq!(offset_of!(RegisterBlock, CLOCK_GROUP), 0x4000);
+ assert_eq!(offset_of!(RegisterBlock, GPR_SHARED), 0x4800);
+ assert_eq!(offset_of!(RegisterBlock, GPR_PRIVATE), 0x4c00);
+ assert_eq!(offset_of!(RegisterBlock, OSC_PLL), 0x5000);
+ assert_eq!(offset_of!(RegisterBlock, LPCG), 0x6000);
+ }
+}
diff --git a/drivers/ccm-11xx/src/ral_1180/clock_root.rs b/drivers/ccm-11xx/src/ral_1180/clock_root.rs
new file mode 100644
index 0000000..ebf8db9
--- /dev/null
+++ b/drivers/ccm-11xx/src/ral_1180/clock_root.rs
@@ -0,0 +1,61 @@
+#[repr(C)]
+#[allow(non_snake_case)]
+pub struct RegisterBlock {
+ pub CONTROL: u32,
+ pub CONTROL_SET: u32,
+ pub CONTROL_CLR: u32,
+ pub CONTROL_TOG: u32,
+ _reserved0: [u8; 16],
+ pub STATUS0: u32,
+ _reserved1: [u8; 12],
+ pub AUTHEN: u32,
+ _reserved2: [u8; 76],
+}
+
+ral_registers::register! {
+ #[doc = "Clock Root Control Register"]
+ pub CONTROL<u32> RW [
+ #[doc = "Clock division fraction."]
+ DIV start(0) width(8) RW {}
+ #[doc = "Clock multiplexer."]
+ MUX start(8) width(2) RW {}
+ #[doc = "Shutdown clock root."]
+ OFF start(24) width(1) RW {}
+ ]
+}
+
+pub use CONTROL as CONTROL_SET;
+pub use CONTROL as CONTROL_CLR;
+pub use CONTROL as CONTROL_TOG;
+
+ral_registers::register! {
+ #[doc = "Clock root working status"]
+ pub STATUS0<u32> RO [
+ #[doc = "Current clock root DIV setting"]
+ DIV start(0) width(8) RO {}
+ #[doc = "Current clock root MUX setting"]
+ MUX start(8) width(2) RO {}
+ #[doc = "Current clock root OFF setting"]
+ OFF start(24) width(1) RO {}
+ #[doc = "Internal updating in generation logic Indication for clock generation logic is applying new setting."]
+ SLICE_BUSY start(28) width(1) RO {}
+ #[doc = "Indication for clock root internal logic is updating. This status is a combination of UPDATE_FORWARD and SLICE_BUSY."]
+ CHANGING start(31) width(1) RO {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Clock root access control"]
+ pub AUTHEN<u32> RW [
+ #[doc = "User access permission"]
+ TZ_USER start(8) width(1) RW {}
+ #[doc = "Non-secure access permission"]
+ TZ_NS start(9) width(1) RW {}
+ #[doc = "Lock TrustZone settings"]
+ LOCK_TZ start(11) width(1) RW {}
+ #[doc = "Lock white list"]
+ LOCK_LIST start(15) width(1) RW {}
+ #[doc = "Whitelist settings"]
+ WHITE_LIST start(16) width(16) RW {}
+ ]
+}
diff --git a/drivers/ccm-11xx/src/ral_1180/lpcg.rs b/drivers/ccm-11xx/src/ral_1180/lpcg.rs
new file mode 100644
index 0000000..21d099f
--- /dev/null
+++ b/drivers/ccm-11xx/src/ral_1180/lpcg.rs
@@ -0,0 +1,15 @@
+#[repr(C)]
+#[allow(non_snake_case)]
+pub struct RegisterBlock {
+ pub DIRECT: u32,
+ _reserved0: [u8; 12],
+ pub LPM0: u32,
+ pub LPM1: u32,
+ _reserved1: [u8; 4],
+ pub LPM_CUR: u32,
+ pub STATUS0: u32,
+ pub STATUS1: u32,
+ _reserved2: [u8; 8],
+ pub AUTHEN: u32,
+ _reserved3: [u8; 12],
+}
diff --git a/drivers/ccm-11xx/src/ral_1180/observe.rs b/drivers/ccm-11xx/src/ral_1180/observe.rs
new file mode 100644
index 0000000..7371acd
--- /dev/null
+++ b/drivers/ccm-11xx/src/ral_1180/observe.rs
@@ -0,0 +1,31 @@
+#[repr(C)]
+#[allow(non_snake_case)]
+pub struct RegisterBlock {
+ pub CONTROL: u32,
+ pub CONTROL_SET: u32,
+ pub CONTROL_CLR: u32,
+ pub CONTROL_TOG: u32,
+ _reserved0: [u8; 16],
+ pub STATUS: u32,
+ _reserved1: [u8; 12],
+ pub AUTHEN: u32,
+ pub AUTHEN_SET: u32,
+ pub AUTHEN_CLR: u32,
+ pub AUTHEN_TOG: u32,
+ pub FREQUENCY_CURRENT: u32,
+ pub FREQUENCY_MIN: u32,
+ pub FREQUENCY_MAX: u32,
+ _reserved2: [u8; 4],
+ pub PERIOD_CURRENT: u32,
+ pub PERIOD_MIN: u32,
+ pub PERIOD_MAX: u32,
+ _reserved3: [u8; 4],
+ pub HIGH_CURRENT: u32,
+ pub HIGH_MIN: u32,
+ pub HIGH_MAX: u32,
+ _reserved4: [u8; 4],
+ pub LOW_CURRENT: u32,
+ pub LOW_MIN: u32,
+ pub LOW_MAX: u32,
+ _reserved5: [u8; 4],
+}
diff --git a/drivers/ccm-11xx/src/ral_1180/osc_pll.rs b/drivers/ccm-11xx/src/ral_1180/osc_pll.rs
new file mode 100644
index 0000000..21d099f
--- /dev/null
+++ b/drivers/ccm-11xx/src/ral_1180/osc_pll.rs
@@ -0,0 +1,15 @@
+#[repr(C)]
+#[allow(non_snake_case)]
+pub struct RegisterBlock {
+ pub DIRECT: u32,
+ _reserved0: [u8; 12],
+ pub LPM0: u32,
+ pub LPM1: u32,
+ _reserved1: [u8; 4],
+ pub LPM_CUR: u32,
+ pub STATUS0: u32,
+ pub STATUS1: u32,
+ _reserved2: [u8; 8],
+ pub AUTHEN: u32,
+ _reserved3: [u8; 12],
+}
diff --git a/drivers/ccm-11xx/src/ral_11xx/clock_group.rs b/drivers/ccm-11xx/src/ral_11xx/clock_group.rs
new file mode 100644
index 0000000..cdef7da
--- /dev/null
+++ b/drivers/ccm-11xx/src/ral_11xx/clock_group.rs
@@ -0,0 +1,33 @@
+pub use super::clock_root::RegisterBlock;
+
+ral_registers::register! {
+ #[doc = "Clock group control"]
+ pub CONTROL<u32> RW [
+ #[doc = "Clock divider0"]
+ DIV0 start(0) width(4) RW {}
+ #[doc = "Clock group global restart count"]
+ RSTDIV start(16) width(8) RW {}
+ #[doc = "OFF"]
+ OFF start(24) width(1) RW {}
+ ]
+}
+
+pub use CONTROL as CONTROL_SET;
+pub use CONTROL as CONTROL_CLR;
+pub use CONTROL as CONTROL_TOG;
+
+pub mod status0 {
+ pub use super::CONTROL::{DIV0, OFF, RSTDIV};
+ pub use crate::STATUS0_RO::*;
+}
+
+pub use super::clock_root::STATUS1;
+
+pub use super::clock_root::CONFIG;
+
+pub use super::clock_root::{AUTHEN, AUTHEN_CLR, AUTHEN_SET, AUTHEN_TOG};
+
+#[allow(non_snake_case)]
+pub mod SETPOINT {
+ pub use super::CONFIG::*;
+}
diff --git a/drivers/ccm-11xx/src/ral_11xx/clock_root.rs b/drivers/ccm-11xx/src/ral_11xx/clock_root.rs
new file mode 100644
index 0000000..68c2f93
--- /dev/null
+++ b/drivers/ccm-11xx/src/ral_11xx/clock_root.rs
@@ -0,0 +1,106 @@
+#[repr(C)]
+#[allow(non_snake_case)]
+pub struct RegisterBlock {
+ pub CONTROL: u32,
+ pub CONTROL_SET: u32,
+ pub CONTROL_CLR: u32,
+ pub CONTROL_TOG: u32,
+ _reserved0: [u8; 16],
+ pub STATUS0: u32,
+ pub STATUS1: u32,
+ _reserved1: [u8; 4],
+ pub CONFIG: u32,
+ pub AUTHEN: u32,
+ pub AUTHEN_SET: u32,
+ pub AUTHEN_CLR: u32,
+ pub AUTHEN_TOG: u32,
+ pub SETPOINT: [u32; 16],
+}
+
+ral_registers::register! {
+ #[doc = "Clock root control"]
+ pub CONTROL<u32> RW [
+ #[doc = "Clock divider"]
+ DIV start(0) width(8) RW {}
+ #[doc = "Clock multiplexer"]
+ MUX start(8) width(3) RW {}
+ #[doc = "OFF"]
+ OFF start(24) width(1) RW {}
+ ]
+}
+
+pub use CONTROL as CONTROL_SET;
+pub use CONTROL as CONTROL_CLR;
+pub use CONTROL as CONTROL_TOG;
+
+#[allow(non_snake_case)]
+pub mod STATUS0 {
+ pub use super::CONTROL::{DIV, MUX, OFF};
+ pub use crate::STATUS0_RO::*;
+}
+
+ral_registers::register! {
+ #[doc = "Clock root low power status"]
+ pub STATUS1<u32> RO [
+ #[doc = "Target Setpoint"]
+ TARGET_SETPOINT start(16) width(4) RO {}
+ #[doc = "Current Setpoint"]
+ CURRENT_SETPOINT start(20) width(4) RO {}
+ #[doc = "Clock frequency decrease request"]
+ DOWN_REQUEST start(24) width(1) RO {}
+ #[doc = "Clock frequency decrease finish"]
+ DOWN_DONE start(25) width(1) RO {}
+ #[doc = "Clock frequency increase request"]
+ UP_REQUEST start(26) width(1) RO {}
+ #[doc = "Clock frequency increase finish"]
+ UP_DONE start(27) width(1) RO {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Clock root configuration"]
+ pub CONFIG<u32> RO [
+ #[doc = "Setpoint present"]
+ SETPOINT_PRESENT start(4) width(1) RO {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Clock root access control"]
+ pub AUTHEN<u32> RW [
+ #[doc = "User access"]
+ TZ_USER start(0) width(1) RW {}
+ #[doc = "Non-secure access"]
+ TZ_NS start(1) width(1) RW {}
+ #[doc = "Lock truszone setting"]
+ LOCK_TZ start(4) width(1) RW {}
+ #[doc = "Whitelist"]
+ WHITE_LIST start(8) width(4) RW {}
+ #[doc = "Lock Whitelist"]
+ LOCK_LIST start(12) width(1) RW {}
+ #[doc = "Low power and access control by domain"]
+ DOMAIN_MODE start(16) width(1) RW {}
+ #[doc = "Low power and access control by Setpoint"]
+ SETPOINT_MODE start(17) width(1) RW {}
+ #[doc = "Lock low power and access mode"]
+ LOCK_MODE start(20) width(1) RW {}
+ ]
+}
+
+pub use AUTHEN as AUTHEN_SET;
+pub use AUTHEN as AUTHEN_CLR;
+pub use AUTHEN as AUTHEN_TOG;
+
+ral_registers::register! {
+ #[doc = "Setpoint setting"]
+ pub SETPOINT<u32> RW [
+ #[doc = "Clock divider"]
+ DIV start(0) width(8) RW {}
+ #[doc = "Clock multiplexer"]
+ MUX start(8) width(3) RW {}
+ #[doc = "OFF"]
+ OFF start(24) width(1) RW {}
+ #[doc = "Grade"]
+ GRADE start(28) width(4) RW {}
+ ]
+}
diff --git a/drivers/ccm-11xx/src/ral_11xx/lpcg.rs b/drivers/ccm-11xx/src/ral_11xx/lpcg.rs
new file mode 100644
index 0000000..9d22646
--- /dev/null
+++ b/drivers/ccm-11xx/src/ral_11xx/lpcg.rs
@@ -0,0 +1,28 @@
+pub use super::osc_pll::RegisterBlock;
+
+pub use super::osc_pll::DIRECT;
+
+pub use super::osc_pll::DOMAIN;
+
+#[allow(non_snake_case)]
+pub mod STATUS0 {
+ pub use super::super::osc_pll::STATUS0::{ACTIVE_DOMAIN, DOMAIN_ENABLE, ON, access};
+}
+
+#[allow(non_snake_case)]
+pub mod STATUS1 {
+ pub use super::super::osc_pll::STATUS1::{
+ CPU0_MODE, CPU0_MODE_DONE, CPU0_MODE_REQUEST, CPU1_MODE, CPU1_MODE_DONE, CPU1_MODE_REQUEST,
+ CPU2_MODE, CPU2_MODE_DONE, CPU2_MODE_REQUEST, CPU3_MODE, CPU3_MODE_DONE, CPU3_MODE_REQUEST,
+ CURRENT_SETPOINT, SETPOINT_OFF_DONE, SETPOINT_OFF_REQUEST, SETPOINT_ON_DONE,
+ SETPOINT_ON_REQUEST, TARGET_SETPOINT, access,
+ };
+}
+
+pub mod config {
+ pub use super::super::clock_root::CONFIG::SETPOINT_PRESENT;
+}
+
+pub use super::osc_pll::SETPOINT;
+
+pub use super::osc_pll::AUTHEN;
diff --git a/drivers/ccm-11xx/src/ral_11xx/osc_pll.rs b/drivers/ccm-11xx/src/ral_11xx/osc_pll.rs
new file mode 100644
index 0000000..c4f10ba
--- /dev/null
+++ b/drivers/ccm-11xx/src/ral_11xx/osc_pll.rs
@@ -0,0 +1,173 @@
+#[repr(C)]
+#[allow(non_snake_case)]
+pub struct RegisterBlock {
+ pub DIRECT: u32,
+ pub DOMAIN: u32,
+ pub SETPOINT: u32,
+ _reserved: [u8; 4],
+ pub STATUS0: u32,
+ pub STATUS1: u32,
+ pub CONFIG: u32,
+ pub AUTHEN: u32,
+}
+
+ral_registers::register! {
+ #[doc = "Clock source direct control"]
+ pub DIRECT<u32> RW [
+ #[doc = "turn on clock source"]
+ ON start(0) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Clock source domain control"]
+ pub DOMAIN<u32> RW [
+ #[doc = "Current dependence level"]
+ LEVEL start(0) width(3) RW {}
+ #[doc = "Dependence level"]
+ LEVEL0 start(16) width(3) RW {}
+ #[doc = "Depend level"]
+ LEVEL1 start(20) width(3) RW {}
+ #[doc = "Depend level"]
+ LEVEL2 start(24) width(3) RW {}
+ #[doc = "Depend level"]
+ LEVEL3 start(28) width(3) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Clock source Setpoint setting"]
+ pub SETPOINT<u32> RW [
+ #[doc = "Setpoint"]
+ SETPOINT start(0) width(16) RW {}
+ #[doc = "Standby"]
+ STANDBY start(16) width(16) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Clock source working status"]
+ pub STATUS0<u32> RO [
+ #[doc = "Clock source current state"]
+ ON start(0) width(1) RO {}
+ #[doc = "Clock source active"]
+ STATUS_EARLY start(4) width(1) RO {}
+ #[doc = "Clock source ready"]
+ STATUS_LATE start(5) width(1) RO {}
+ #[doc = "Domains that own this clock source"]
+ ACTIVE_DOMAIN start(8) width(4) RO {}
+ #[doc = "Enable status from each domain"]
+ DOMAIN_ENABLE start(12) width(4) RO {}
+ #[doc = "In use"]
+ IN_USE start(28) width(1) RO {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Clock source low power status"]
+ pub STATUS1<u32> RO [
+ #[doc = "Domain0 Low Power Mode"]
+ CPU0_MODE start(0) width(2) RO {
+ #[doc = "Run"]
+ RUN = 0,
+ #[doc = "Wait"]
+ WAIT = 0x1,
+ #[doc = "Stop"]
+ STOP = 0x2,
+ #[doc = "Suspend"]
+ SUSPEND = 0x3,
+ }
+ #[doc = "Domain0 request enter Low Power Mode"]
+ CPU0_MODE_REQUEST start(2) width(1) RO {}
+ #[doc = "Domain0 Low Power Mode task done"]
+ CPU0_MODE_DONE start(3) width(1) RO {}
+ #[doc = "Domain1 Low Power Mode"]
+ CPU1_MODE start(4) width(2) RO {
+ #[doc = "Run"]
+ RUN = 0,
+ #[doc = "Wait"]
+ WAIT = 0x1,
+ #[doc = "Stop"]
+ STOP = 0x2,
+ #[doc = "Suspend"]
+ SUSPEND = 0x3,
+ }
+ #[doc = "Domain1 request enter Low Power Mode"]
+ CPU1_MODE_REQUEST start(6) width(1) RO {}
+ #[doc = "Domain1 Low Power Mode task done"]
+ CPU1_MODE_DONE start(7) width(1) RO {}
+ #[doc = "Domain2 Low Power Mode"]
+ CPU2_MODE start(8) width(2) RO {
+ #[doc = "Run"]
+ RUN = 0,
+ #[doc = "Wait"]
+ WAIT = 0x1,
+ #[doc = "Stop"]
+ STOP = 0x2,
+ #[doc = "Suspend"]
+ SUSPEND = 0x3,
+ }
+ #[doc = "Domain2 request enter Low Power Mode"]
+ CPU2_MODE_REQUEST start(10) width(1) RO {}
+ #[doc = "Domain2 Low Power Mode task done"]
+ CPU2_MODE_DONE start(11) width(1) RO {}
+ #[doc = "Domain3 Low Power Mode"]
+ CPU3_MODE start(12) width(2) RO {
+ #[doc = "Run"]
+ RUN = 0,
+ #[doc = "Wait"]
+ WAIT = 0x1,
+ #[doc = "Stop"]
+ STOP = 0x2,
+ #[doc = "Suspend"]
+ SUSPEND = 0x3,
+ }
+ #[doc = "Domain3 request enter Low Power Mode"]
+ CPU3_MODE_REQUEST start(14) width(1) RO {}
+ #[doc = "Domain3 Low Power Mode task done"]
+ CPU3_MODE_DONE start(15) width(1) RO {}
+ #[doc = "Next Setpoint to change to"]
+ TARGET_SETPOINT start(16) width(4) RO {}
+ #[doc = "Current Setpoint"]
+ CURRENT_SETPOINT start(20) width(4) RO {}
+ #[doc = "Clock gate turn off request from GPC Setpoint"]
+ SETPOINT_OFF_REQUEST start(24) width(1) RO {}
+ #[doc = "Clock source turn off finish from GPC Setpoint"]
+ SETPOINT_OFF_DONE start(25) width(1) RO {}
+ #[doc = "Clock gate turn on request from GPC Setpoint"]
+ SETPOINT_ON_REQUEST start(26) width(1) RO {}
+ #[doc = "Clock gate turn on finish from GPC Setpoint"]
+ SETPOINT_ON_DONE start(27) width(1) RO {}
+ #[doc = "Clock gate turn off request from GPC standby"]
+ STANDBY_IN_REQUEST start(28) width(1) RO {}
+ #[doc = "Clock source turn off finish from GPC standby"]
+ STANDBY_IN_DONE start(29) width(1) RO {}
+ #[doc = "Clock gate turn on finish from GPC standby"]
+ STANDBY_OUT_DONE start(30) width(1) RO {}
+ #[doc = "Clock gate turn on request from GPC standby"]
+ STANDBY_OUT_REQUEST start(31) width(1) RO {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Clock source configuration"]
+ pub CONFIG<u32> RO [
+ #[doc = "Automode Present"]
+ AUTOMODE_PRESENT start(1) width(1) RO {}
+ #[doc = "Setpoint present"]
+ SETPOINT_PRESENT start(4) width(1) RO {}
+ ]
+}
+
+#[allow(non_snake_case)]
+pub mod AUTHEN {
+ pub use super::super::clock_root::AUTHEN::*;
+ #[allow(non_upper_case_globals)]
+ pub mod CPULPM {
+ pub const access: ral_registers::Access = ral_registers::Access::RW;
+ pub const offset: u32 = 18;
+ pub const mask: u32 = 1 << 18;
+ #[doc(hidden)]
+ pub mod vals {}
+ }
+}
diff --git a/drivers/ccm-11xx/src/ral_11xx/pll.rs b/drivers/ccm-11xx/src/ral_11xx/pll.rs
new file mode 100644
index 0000000..d5e04d4
--- /dev/null
+++ b/drivers/ccm-11xx/src/ral_11xx/pll.rs
@@ -0,0 +1,598 @@
+use core::num::NonZero;
+
+pub type Instance = ral_registers::Instance<RegisterBlock>;
+
+#[repr(C)]
+#[allow(non_snake_case)]
+pub struct RegisterBlock {
+ reserved_0: [u8; 512],
+ pub ARM_PLL_CTRL: u32,
+ reserved_1: [u8; 12],
+ pub SYS_PLL3_CTRL: u32,
+ reserved_2: [u8; 12],
+ pub SYS_PLL3_UPDATE: u32,
+ reserved_3: [u8; 12],
+ pub SYS_PLL3_PFD: u32,
+ reserved_4: [u8; 12],
+ pub SYS_PLL2_CTRL: u32,
+ reserved_5: [u8; 12],
+ pub SYS_PLL2_UPDATE: u32,
+ reserved_6: [u8; 12],
+ pub SYS_PLL2_SS: u32,
+ reserved_7: [u8; 12],
+ pub SYS_PLL2_PFD: u32,
+ reserved_8: [u8; 44],
+ pub SYS_PLL2_MFD: u32,
+ reserved_9: [u8; 12],
+ pub SYS_PLL1_SS: u32,
+ reserved_10: [u8; 12],
+ pub SYS_PLL1_CTRL: u32,
+ reserved_11: [u8; 12],
+ pub SYS_PLL1_DENOMINATOR: u32,
+ reserved_12: [u8; 12],
+ pub SYS_PLL1_NUMERATOR: u32,
+ reserved_13: [u8; 12],
+ pub SYS_PLL1_DIV_SELECT: u32,
+ reserved_14: [u8; 12],
+ pub PLL_AUDIO_CTRL: u32,
+ reserved_15: [u8; 12],
+ pub PLL_AUDIO_SS: u32,
+ reserved_16: [u8; 12],
+ pub PLL_AUDIO_DENOMINATOR: u32,
+ reserved_17: [u8; 12],
+ pub PLL_AUDIO_NUMERATOR: u32,
+ reserved_18: [u8; 12],
+ pub PLL_AUDIO_DIV_SELECT: u32,
+ reserved_19: [u8; 12],
+ pub PLL_VIDEO_CTRL: u32,
+ reserved_20: [u8; 12],
+ pub PLL_VIDEO_SS: u32,
+ reserved_21: [u8; 12],
+ pub PLL_VIDEO_DENOMINATOR: u32,
+ reserved_22: [u8; 12],
+ pub PLL_VIDEO_NUMERATOR: u32,
+ reserved_23: [u8; 12],
+ pub PLL_VIDEO_DIV_SELECT: u32,
+}
+
+ral_registers::register! {
+ #[doc = "SYS_PLL1_CTRL_REGISTER"]
+ pub SYS_PLL1_CTRL<u32> RW [
+ #[doc = "ENABLE_CLK"]
+ ENABLE_CLK start(13) width(1) RW {}
+ #[doc = "SYS_PLL1_GATE"]
+ SYS_PLL1_GATE start(14) width(1) RW {}
+ #[doc = "SYS_PLL1_DIV2"]
+ SYS_PLL1_DIV2 start(25) width(1) RW {}
+ #[doc = "SYS_PLL1_DIV5"]
+ SYS_PLL1_DIV5 start(26) width(1) RW {}
+ #[doc = "SYS_PLL1_DIV5_CONTROL_MODE"]
+ SYS_PLL1_DIV5_CONTROL_MODE start(27) width(1) RW {
+ #[doc = "Software Mode (Default)"]
+ SW = 0,
+ #[doc = "GPC Mode"]
+ GPC = 0x1,
+ }
+ #[doc = "SYS_PLL1_DIV2_CONTROL_MODE"]
+ SYS_PLL1_DIV2_CONTROL_MODE start(28) width(1) RW {
+ #[doc = "Software Mode (Default)"]
+ SW = 0,
+ #[doc = "GPC Mode"]
+ GPC = 0x1,
+ }
+ #[doc = "SYS_PLL1_STABLE"]
+ SYS_PLL1_STABLE start(29) width(1) RO {}
+ #[doc = "SYS_PLL1_AI_BUSY"]
+ SYS_PLL1_AI_BUSY start(30) width(1) RO {}
+ #[doc = "SYS_PLL1_CONTROL_MODE"]
+ SYS_PLL1_CONTROL_MODE start(31) width(1) RW {
+ #[doc = "Software Mode (Default)"]
+ SW = 0,
+ #[doc = "GPC Mode"]
+ GPC = 0x1,
+ }
+ ]
+}
+
+/// Manage all SYS_PLL1 clocks using setpoints.
+pub fn enable_sys_pll1_setpoints(pll: ral_registers::Instance<RegisterBlock>) {
+ ral_registers::modify_reg!(self, pll, SYS_PLL1_CTRL,
+ SYS_PLL1_CONTROL_MODE: GPC,
+ SYS_PLL1_DIV2_CONTROL_MODE: GPC,
+ SYS_PLL1_DIV5_CONTROL_MODE: GPC,
+ );
+}
+
+ral_registers::register! {
+ #[doc = "ARM_PLL_CTRL_REGISTER"]
+ pub ARM_PLL_CTRL<u32> RW [
+ #[doc = "DIV_SELECT"]
+ DIV_SELECT start(0) width(8) RW {}
+ #[doc = "PLL Start up initialization"]
+ HOLD_RING_OFF start(12) width(1) RW {}
+ #[doc = "Powers up the PLL."]
+ POWERUP start(13) width(1) RW {}
+ #[doc = "Enable the clock output."]
+ ENABLE_CLK start(14) width(1) RW {}
+ #[doc = "POST_DIV_SEL"]
+ POST_DIV_SEL start(15) width(2) RW {
+ #[doc = "Divide by 2"]
+ DIV2 = 0,
+ #[doc = "Divide by 4"]
+ DIV4 = 0x1,
+ #[doc = "Divide by 8"]
+ DIV8 = 0x2,
+ #[doc = "Divide by 1"]
+ DIV1 = 0x3,
+ }
+ #[doc = "Bypass the pll."]
+ BYPASS start(17) width(1) RW {}
+ #[doc = "ARM_PLL_STABLE"]
+ ARM_PLL_STABLE start(29) width(1) RO {}
+ #[doc = "ARM_PLL_GATE"]
+ ARM_PLL_GATE start(30) width(1) RW {}
+ #[doc = "pll_arm_control_mode"]
+ ARM_PLL_CONTROL_MODE start(31) width(1) RW {
+ #[doc = "Software Mode (Default)"]
+ SW = 0,
+ #[doc = "GPC Mode"]
+ GPC = 0x1,
+ }
+ ]
+}
+
+/// Post divider for the ARM PLL.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(u32)]
+pub enum ArmPllPostDiv {
+ /// Divide by 2.
+ Div2 = 0,
+ /// Divide by 4.
+ Div4 = 1,
+ /// Divide by 8.
+ Div8 = 2,
+ /// Divide by 1.
+ Div1 = 3,
+}
+
+impl ArmPllPostDiv {
+ /// Return the divisor for this post divider.
+ #[must_use]
+ pub const fn divisor(self) -> u32 {
+ match self {
+ Self::Div2 => 2,
+ Self::Div4 => 4,
+ Self::Div8 => 8,
+ Self::Div1 => 1,
+ }
+ }
+}
+
+/// The ARM PLL loop divider.
+///
+/// This value is always clamped between 104 and 208.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(transparent)]
+pub struct ArmPllDivSelect(NonZero<u32>);
+
+impl ArmPllDivSelect {
+ /// Construct a loop divider.
+ ///
+ /// The implementation clamps the value between 104 and 208.
+ #[must_use]
+ pub const fn new(div_select: u32) -> Option<Self> {
+ if 104 <= div_select && div_select <= 208 {
+ Some(Self(NonZero::new(div_select).unwrap()))
+ } else {
+ None
+ }
+ }
+ /// Returns the divider as a raw value.
+ #[must_use]
+ #[inline]
+ pub const fn get(self) -> u32 {
+ self.0.get()
+ }
+}
+
+/// Manage the ARM PLL with setpoints.
+///
+/// `post_div` is the post divider selection applied when the
+/// PLL is enabled. Similarly `div_select` is the loop divider.
+/// Use [`arm_pll_frequency`] to understand the expected frequency
+/// affected by this call.
+pub fn enable_arm_pll_setpoints(
+ pll: ral_registers::Instance<RegisterBlock>,
+ post_div: ArmPllPostDiv,
+ div_select: ArmPllDivSelect,
+) {
+ ral_registers::modify_reg!(self, pll, ARM_PLL_CTRL,
+ DIV_SELECT: div_select.get(),
+ POST_DIV_SEL: post_div as u32,
+ ARM_PLL_GATE: 1,
+ ARM_PLL_CONTROL_MODE: GPC,
+ ENABLE_CLK: 0,
+ );
+}
+
+/// Computes the expected ARM PLL frequency.
+pub const fn arm_pll_frequency(post_div: ArmPllPostDiv, div_select: ArmPllDivSelect) -> u32 {
+ 24_000_000 / 2 / post_div.divisor() * div_select.get()
+}
+
+ral_registers::register! {
+ #[doc = "SYS_PLL2_CTRL_REGISTER"]
+ pub SYS_PLL2_CTRL<u32> RW [
+ #[doc = "Enable Internal PLL Regulator"]
+ PLL_REG_EN start(3) width(1) RW {}
+ #[doc = "PLL Start up initialization"]
+ HOLD_RING_OFF start(11) width(1) RW {}
+ #[doc = "Enable the clock output."]
+ ENABLE_CLK start(13) width(1) RW {}
+ #[doc = "Bypass the pll."]
+ BYPASS start(16) width(1) RW {}
+ #[doc = "DITHER_ENABLE"]
+ DITHER_ENABLE start(17) width(1) RW {}
+ #[doc = "PFD_OFFSET_EN"]
+ PFD_OFFSET_EN start(18) width(1) RW {}
+ #[doc = "PLL_DDR_OVERRIDE"]
+ PLL_DDR_OVERRIDE start(19) width(1) RW {}
+ #[doc = "Powers up the PLL."]
+ POWERUP start(23) width(1) RW {}
+ #[doc = "SYS_PLL2_STABLE"]
+ SYS_PLL2_STABLE start(29) width(1) RO {}
+ #[doc = "SYS_PLL2_GATE"]
+ SYS_PLL2_GATE start(30) width(1) RW {}
+ #[doc = "SYS_PLL2_control_mode"]
+ SYS_PLL2_CONTROL_MODE start(31) width(1) RW {
+ #[doc = "Software Mode (Default)"]
+ SW = 0,
+ #[doc = "GPC Mode"]
+ GPC = 0x1,
+ }
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "SYS_PLL2_UPDATE_REGISTER"]
+ pub SYS_PLL2_UPDATE<u32> RW [
+ #[doc = "PFD0_UPDATE"]
+ PFD0_UPDATE start(1) width(1) RW {}
+ #[doc = "PFD1_UPDATE"]
+ PFD1_UPDATE start(2) width(1) RW {}
+ #[doc = "PFD2_UPDATE"]
+ PFD2_UPDATE start(3) width(1) RW {}
+ #[doc = "PFD3_UPDATE"]
+ PFD3_UPDATE start(4) width(1) RW {}
+ #[doc = "pfd0_control_mode"]
+ PFD0_CONTROL_MODE start(5) width(1) RW {
+ #[doc = "Software Mode (Default)"]
+ SW = 0,
+ #[doc = "GPC Mode"]
+ GPC = 0x1,
+ }
+ #[doc = "pfd1_control_mode"]
+ PFD1_CONTROL_MODE start(6) width(1) RW {
+ #[doc = "Software Mode (Default)"]
+ SW = 0,
+ #[doc = "GPC Mode"]
+ GPC = 0x1,
+ }
+ #[doc = "pfd2_control_mode"]
+ PFD2_CONTROL_MODE start(7) width(1) RW {
+ #[doc = "Software Mode (Default)"]
+ SW = 0,
+ #[doc = "GPC Mode"]
+ GPC = 0x1,
+ }
+ #[doc = "pfd3_control_mode"]
+ PFD3_CONTROL_MODE start(8) width(1) RW {
+ #[doc = "Software Mode (Default)"]
+ SW = 0,
+ #[doc = "GPC Mode"]
+ GPC = 0x1,
+ }
+ ]
+}
+
+/// Manage SYS_PLL2 clocks and PFDs using setpoints.
+pub fn enable_sys_pll2_setpoints(pll: ral_registers::Instance<RegisterBlock>) {
+ ral_registers::modify_reg!(self, pll, SYS_PLL2_UPDATE,
+ PFD3_CONTROL_MODE: GPC,
+ PFD2_CONTROL_MODE: GPC,
+ PFD1_CONTROL_MODE: GPC,
+ PFD0_CONTROL_MODE: GPC,
+ );
+ ral_registers::modify_reg!(self, pll, SYS_PLL2_CTRL,
+ SYS_PLL2_CONTROL_MODE: GPC,
+ SYS_PLL2_GATE: 1,
+ ENABLE_CLK: 0,
+ );
+}
+
+ral_registers::register! {
+ #[doc = "SYS_PLL3_CTRL_REGISTER"]
+ pub SYS_PLL3_CTRL<u32> RW [
+ #[doc = "SYS PLL3 DIV2 gate"]
+ SYS_PLL3_DIV2 start(3) width(1) RW {}
+ #[doc = "Enable Internal PLL Regulator"]
+ PLL_REG_EN start(4) width(1) RW {}
+ #[doc = "PLL Start up initialization"]
+ HOLD_RING_OFF start(11) width(1) RW {}
+ #[doc = "Enable the clock output."]
+ ENABLE_CLK start(13) width(1) RW {}
+ #[doc = "BYPASS"]
+ BYPASS start(16) width(1) RW {}
+ #[doc = "Powers up the PLL."]
+ POWERUP start(21) width(1) RW {}
+ #[doc = "SYS_PLL3_DIV2_CONTROL_MODE"]
+ SYS_PLL3_DIV2_CONTROL_MODE start(28) width(1) RW {
+ #[doc = "Software Mode (Default)"]
+ SW = 0,
+ #[doc = "GPC Mode"]
+ GPC = 0x1,
+ }
+ #[doc = "SYS_PLL3_STABLE"]
+ SYS_PLL3_STABLE start(29) width(1) RO {}
+ #[doc = "SYS_PLL3_GATE"]
+ SYS_PLL3_GATE start(30) width(1) RW {}
+ #[doc = "SYS_PLL3_control_mode"]
+ SYS_PLL3_CONTROL_MODE start(31) width(1) RW {
+ #[doc = "Software Mode (Default)"]
+ SW = 0,
+ #[doc = "GPC Mode"]
+ GPC = 0x1,
+ }
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "SYS_PLL3_UPDATE_REGISTER"]
+ pub SYS_PLL3_UPDATE<u32> RW [
+ #[doc = "PFD0_OVERRIDE"]
+ PFD0_UPDATE start(1) width(1) RW {}
+ #[doc = "PFD1_OVERRIDE"]
+ PFD1_UPDATE start(2) width(1) RW {}
+ #[doc = "PFD2_OVERRIDE"]
+ PFD2_UPDATE start(3) width(1) RW {}
+ #[doc = "PFD3_UPDATE"]
+ PFD3_UPDATE start(4) width(1) RW {}
+ #[doc = "pfd0_control_mode"]
+ PFD0_CONTROL_MODE start(5) width(1) RW {
+ #[doc = "Software Mode (Default)"]
+ SW = 0,
+ #[doc = "GPC Mode"]
+ GPC = 0x1,
+ }
+ #[doc = "pfd1_control_mode"]
+ PFD1_CONTROL_MODE start(6) width(1) RW {
+ #[doc = "Software Mode (Default)"]
+ SW = 0,
+ #[doc = "GPC Mode"]
+ GPC = 0x1,
+ }
+ #[doc = "pdf2_control_mode"]
+ PFD2_CONTROL_MODE start(7) width(1) RW {
+ #[doc = "Software Mode (Default)"]
+ SW = 0,
+ #[doc = "GPC Mode"]
+ GPC = 0x1,
+ }
+ #[doc = "pfd3_control_mode"]
+ PFD3_CONTROL_MODE start(8) width(1) RW {
+ #[doc = "Software Mode (Default)"]
+ SW = 0,
+ #[doc = "GPC Mode"]
+ GPC = 0x1,
+ }
+ ]
+}
+
+/// Manage SYS_PLL3 clocks and PFDs using setpoints.
+pub fn enable_sys_pll3_setpoints(pll: ral_registers::Instance<RegisterBlock>) {
+ ral_registers::modify_reg!(self, pll, SYS_PLL3_UPDATE,
+ PFD3_CONTROL_MODE: GPC,
+ PFD2_CONTROL_MODE: GPC,
+ PFD1_CONTROL_MODE: GPC,
+ PFD0_CONTROL_MODE: GPC,
+ );
+ ral_registers::modify_reg!(self, pll, SYS_PLL3_CTRL,
+ SYS_PLL3_CONTROL_MODE: GPC,
+ SYS_PLL3_DIV2_CONTROL_MODE: GPC,
+ SYS_PLL3_GATE: 1,
+ ENABLE_CLK: 0,
+ );
+}
+
+ral_registers::register! {
+ #[doc = "SYS_PLL3_PFD_REGISTER"]
+ pub SYS_PLL3_PFD<u32> RW [
+ #[doc = "PFD0_FRAC"]
+ PFD0_FRAC start(0) width(6) RW {}
+ #[doc = "PFD0_STABLE"]
+ PFD0_STABLE start(6) width(1) RO {}
+ #[doc = "PFD0_DIV1_CLKGATE"]
+ PFD0_DIV1_CLKGATE start(7) width(1) RW {}
+ #[doc = "PFD1_FRAC"]
+ PFD1_FRAC start(8) width(6) RW {}
+ #[doc = "PFD1_STABLE"]
+ PFD1_STABLE start(14) width(1) RO {}
+ #[doc = "PFD1_DIV1_CLKGATE"]
+ PFD1_DIV1_CLKGATE start(15) width(1) RW {}
+ #[doc = "PFD2_FRAC"]
+ PFD2_FRAC start(16) width(6) RW {}
+ #[doc = "PFD2_STABLE"]
+ PFD2_STABLE start(22) width(1) RO {}
+ #[doc = "PFD2_DIV1_CLKGATE"]
+ PFD2_DIV1_CLKGATE start(23) width(1) RW {}
+ #[doc = "PFD3_FRAC"]
+ PFD3_FRAC start(24) width(6) RW {}
+ #[doc = "PFD3_STABLE"]
+ PFD3_STABLE start(30) width(1) RO {}
+ #[doc = "PFD3_DIV1_CLKGATE"]
+ PFD3_DIV1_CLKGATE start(31) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "SYS_PLL2_PFD_REGISTER"]
+ pub SYS_PLL2_PFD<u32> RW [
+ #[doc = "PFD0_FRAC"]
+ PFD0_FRAC start(0) width(6) RW {}
+ #[doc = "PFD0_STABLE"]
+ PFD0_STABLE start(6) width(1) RO {}
+ #[doc = "PFD0_DIV1_CLKGATE"]
+ PFD0_DIV1_CLKGATE start(7) width(1) RW {}
+ #[doc = "PFD1_FRAC"]
+ PFD1_FRAC start(8) width(6) RW {}
+ #[doc = "PFD1_STABLE"]
+ PFD1_STABLE start(14) width(1) RO {}
+ #[doc = "PFD1_DIV1_CLKGATE"]
+ PFD1_DIV1_CLKGATE start(15) width(1) RW {}
+ #[doc = "PFD2_FRAC"]
+ PFD2_FRAC start(16) width(6) RW {}
+ #[doc = "PFD2_STABLE"]
+ PFD2_STABLE start(22) width(1) RO {}
+ #[doc = "PFD2_DIV1_CLKGATE"]
+ PFD2_DIV1_CLKGATE start(23) width(1) RW {}
+ #[doc = "PFD3_FRAC"]
+ PFD3_FRAC start(24) width(6) RW {}
+ #[doc = "PFD3_STABLE"]
+ PFD3_STABLE start(30) width(1) RO {}
+ #[doc = "PFD3_DIV1_CLKGATE"]
+ PFD3_DIV1_CLKGATE start(31) width(1) RW {}
+ ]
+}
+
+/// A fractional divider for a PFD.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(transparent)]
+pub struct PfdFrac(NonZero<u8>);
+
+impl PfdFrac {
+ /// Create a new PDF fractional divider.
+ ///
+ /// Returns `None` if `div` is not between 12 and 35.
+ pub const fn new(div: u8) -> Option<Self> {
+ // Safety: div is between 12 and 35.
+ unsafe {
+ if 12 <= div && div <= 35 {
+ Some(Self(NonZero::new_unchecked(div)))
+ } else {
+ None
+ }
+ }
+ }
+
+ /// Similar to `new`, but this will automatically clamp
+ /// the value between the 12 and 35.
+ pub const fn clamp(div: u8) -> Self {
+ // Safety: div is clamped between 12 and 35.
+ unsafe {
+ Self(NonZero::new_unchecked(if div < 12 {
+ 12
+ } else if div > 35 {
+ 35
+ } else {
+ div
+ }))
+ }
+ }
+
+ /// Return the inner value.
+ #[inline]
+ pub const fn get(self) -> u8 {
+ self.0.get()
+ }
+}
+
+/// Returns the PLL3 PFD fractional dividers.
+///
+/// If a divider is invalid, the implementation returns `None`
+/// for that divider.
+pub fn pll3_pfd_fracs(pll: ral_registers::Instance<RegisterBlock>) -> [Option<PfdFrac>; 4] {
+ let (pfd0, pfd1, pfd2, pfd3) = ral_registers::read_reg!(
+ self,
+ pll,
+ SYS_PLL3_PFD,
+ PFD0_FRAC,
+ PFD1_FRAC,
+ PFD2_FRAC,
+ PFD3_FRAC
+ );
+
+ [
+ PfdFrac::new(pfd0 as _),
+ PfdFrac::new(pfd1 as _),
+ PfdFrac::new(pfd2 as _),
+ PfdFrac::new(pfd3 as _),
+ ]
+}
+
+/// Set the PLL3 PFD fractional dividers.
+///
+/// The call updates all four dividers without affecting the gating.
+/// However, it does not update the values.
+pub fn set_pll3_pfd_fracs(pll: ral_registers::Instance<RegisterBlock>, pfds: [PfdFrac; 4]) {
+ ral_registers::modify_reg!(self, pll, SYS_PLL3_PFD,
+ PFD3_FRAC: pfds[3].get() as u32,
+ PFD2_FRAC: pfds[2].get() as u32,
+ PFD1_FRAC: pfds[1].get() as u32,
+ PFD0_FRAC: pfds[0].get() as u32,
+ );
+}
+
+/// Update the PFD fractional divider values that were previously
+/// set.
+///
+/// Only those that have a `true` update are updated.
+pub fn update_pll3_pfd_fracs(pll: ral_registers::Instance<RegisterBlock>, update: [bool; 4]) {
+ ral_registers::modify_reg!(self, pll, SYS_PLL3_UPDATE,
+ PFD3_UPDATE: update[3] as u32,
+ PFD2_UPDATE: update[2] as u32,
+ PFD1_UPDATE: update[1] as u32,
+ PFD0_UPDATE: update[0] as u32,
+ );
+ ral_registers::modify_reg!(self, pll, SYS_PLL3_UPDATE,
+ PFD3_UPDATE: false as u32,
+ PFD2_UPDATE: false as u32,
+ PFD1_UPDATE: false as u32,
+ PFD0_UPDATE: false as u32,
+ );
+}
+
+/// Returns the PLL2 PFD fractional dividers.
+///
+/// If a divider is invalid, the implementation returns `None`
+/// for that divider.
+pub fn pll2_pfd_fracs(pll: ral_registers::Instance<RegisterBlock>) -> [Option<PfdFrac>; 4] {
+ let (pfd0, pfd1, pfd2, pfd3) = ral_registers::read_reg!(
+ self,
+ pll,
+ SYS_PLL2_PFD,
+ PFD0_FRAC,
+ PFD1_FRAC,
+ PFD2_FRAC,
+ PFD3_FRAC
+ );
+
+ [
+ PfdFrac::new(pfd0 as _),
+ PfdFrac::new(pfd1 as _),
+ PfdFrac::new(pfd2 as _),
+ PfdFrac::new(pfd3 as _),
+ ]
+}
+
+/// Set the PLL2 PFD fractional dividers.
+///
+/// The call updates all four dividers without affecting the gating.
+/// However, it does not update the values.
+pub fn set_pll2_pfd_fracs(pll: ral_registers::Instance<RegisterBlock>, pfds: [PfdFrac; 4]) {
+ ral_registers::modify_reg!(self, pll, SYS_PLL2_PFD,
+ PFD3_FRAC: pfds[3].get() as u32,
+ PFD2_FRAC: pfds[2].get() as u32,
+ PFD1_FRAC: pfds[1].get() as u32,
+ PFD0_FRAC: pfds[0].get() as u32,
+ );
+}
diff --git a/drivers/dcdc/Cargo.toml b/drivers/dcdc/Cargo.toml
new file mode 100644
index 0000000..5ed2fd6
--- /dev/null
+++ b/drivers/dcdc/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "imxrt-drivers-dcdc"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+ral-registers = { workspace = true }
diff --git a/drivers/dcdc/src/lib.rs b/drivers/dcdc/src/lib.rs
new file mode 100644
index 0000000..ab2b814
--- /dev/null
+++ b/drivers/dcdc/src/lib.rs
@@ -0,0 +1,43 @@
+#![no_std]
+
+pub type Instance = ral_registers::Instance<RegisterBlock>;
+
+#[repr(C)]
+#[allow(non_snake_case)]
+pub struct RegisterBlock {
+ pub REG0: u32,
+ pub REG1: u32,
+ pub REG2: u32,
+ pub REG3: u32,
+}
+
+pub type DCDC = ral_registers::Instance<RegisterBlock>;
+
+/// Set the target value of `VDD_SOC`, in millivolts
+///
+/// Values are clamped between 800mV and 1575mV, with 25mV step
+/// sizes.
+pub fn set_target_vdd_soc(dcdc: DCDC, millivolts: u32) {
+ let mv = millivolts.clamp(800, 1575);
+ let trg = (mv - 800) / 25;
+ ral_registers::modify_reg!(self, dcdc, REG3, TRG: trg);
+ while ral_registers::read_reg!(self, dcdc, REG0, STS_DC_OK == 0) {}
+}
+
+/// Returns the target value of `VDD_SOC`, in millivolts.
+pub fn target_vdd_soc(dcdc: DCDC) -> u32 {
+ let trg = ral_registers::read_reg!(self, dcdc, REG3, TRG);
+ trg * 25 + 800
+}
+
+ral_registers::register! {
+ pub REG3<u32> RW [
+ TRG start(0) width(5) RW {}
+ ]
+}
+
+ral_registers::register! {
+ pub REG0<u32> RW [
+ STS_DC_OK start(31) width(1) RW {}
+ ]
+}
diff --git a/drivers/edma/Cargo.toml b/drivers/edma/Cargo.toml
new file mode 100644
index 0000000..dc9086c
--- /dev/null
+++ b/drivers/edma/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "imxrt-drivers-edma"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+ral-registers = { workspace = true }
diff --git a/drivers/edma/src/dma.rs b/drivers/edma/src/dma.rs
new file mode 100644
index 0000000..8f4cb9a
--- /dev/null
+++ b/drivers/edma/src/dma.rs
@@ -0,0 +1,161 @@
+//! DMA controllers.
+
+pub mod edma {
+ use crate::tcd::edma as tcd;
+
+ #[repr(C)]
+ #[allow(non_snake_case)]
+ pub struct RegisterBlock {
+ /// Control Register
+ pub CR: u32,
+ /// Error Status Register
+ pub ES: u32,
+ _reserved1: [u32; 1],
+ /// Enable Request Register
+ pub ERQ: u32,
+ _reserved2: [u32; 1],
+ /// Enable Error Interrupt Register
+ pub EEI: u32,
+ /// Clear Enable Error Interrupt Register
+ pub CEEI: u8,
+ /// Set Enable Error Interrupt Register
+ pub SEEI: u8,
+ /// Clear Enable Request Register
+ pub CERQ: u8,
+ /// Set Enable Request Register
+ pub SERQ: u8,
+ /// Clear DONE Status Bit Register
+ pub CDNE: u8,
+ /// Set START Bit Register
+ pub SSRT: u8,
+ /// Clear Error Register
+ pub CERR: u8,
+ /// Clear Interrupt Request Register
+ pub CINT: u8,
+ _reserved3: [u32; 1],
+ /// Interrupt Request Register
+ pub INT: u32,
+ _reserved4: [u32; 1],
+ /// Error Register
+ pub ERR: u32,
+ _reserved5: [u32; 1],
+ /// Hardware Request Status Register
+ pub HRS: u32,
+ _reserved6: [u32; 3],
+ /// Enable Asynchronous Request in Stop Register
+ pub EARS: u32,
+ _reserved7: [u32; 46],
+ /// Channel Priority Registers
+ pub DCHPRI: [u8; 32],
+ _reserved8: [u32; 952],
+ /// Transfer Control Descriptors
+ pub TCD: [tcd::RegisterBlock; 32],
+ }
+
+ const _: () = assert!(core::mem::offset_of!(RegisterBlock, DCHPRI) == 0x100);
+ const _: () = assert!(core::mem::offset_of!(RegisterBlock, TCD) == 0x1000);
+
+ /// Produce an index into `DCHPRI` for the given channel.
+ pub const fn dchpri_index(channel: usize) -> usize {
+ const X: [usize; 32] = [
+ 3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12, 19, 18, 17, 16, 23, 22, 21, 20,
+ 27, 26, 25, 24, 31, 30, 29, 28,
+ ];
+ X[channel]
+ }
+
+ ral_registers::register! {
+ pub SERQ<u8> WO []
+ }
+
+ ral_registers::register! {
+ pub CERQ<u8> WO []
+ }
+
+ ral_registers::register! {
+ pub HRS<u32> RO []
+ }
+
+ ral_registers::register! {
+ pub INT<u32> RW []
+ }
+
+ ral_registers::register! {
+ pub CINT<u8> WO []
+ }
+
+ ral_registers::register! {
+ pub CDNE<u8> WO []
+ }
+
+ ral_registers::register! {
+ pub ERR<u32> RW []
+ }
+
+ ral_registers::register! {
+ pub CERR<u8> WO []
+ }
+
+ ral_registers::register! {
+ pub ERQ<u32> RW []
+ }
+}
+
+pub mod edma3 {
+ use crate::tcd::edma34 as tcd;
+
+ #[repr(C)]
+ #[allow(non_snake_case)]
+ pub struct RegisterBlock {
+ pub CSR: u32,
+ pub ES: u32,
+ pub INT: u32,
+ pub HRS: u32,
+ _reserved0: [u8; 0x100 - 0x10],
+ pub GRPRI: [u32; 32],
+ _reserved1: [u8; 0x1_0000 - 0x180],
+ pub TCD: [tcd::RegisterBlock; 32],
+ }
+
+ const _: () = assert!(core::mem::offset_of!(RegisterBlock, GRPRI) == 0x100);
+ const _: () = assert!(core::mem::offset_of!(RegisterBlock, TCD) == 0x1_0000);
+
+ ral_registers::register! {
+ pub CSR<u32> RW [
+ GMRC start(7) width(1) RW {}
+ ]
+ }
+
+ ral_registers::register! {
+ pub HRS<u32> RO []
+ }
+
+ ral_registers::register! {
+ pub INT<u32> RO []
+ }
+}
+
+pub mod edma4 {
+ use crate::tcd::edma34 as tcd;
+
+ #[repr(C)]
+ #[allow(non_snake_case)]
+ pub struct RegisterBlock {
+ pub CSR: u32,
+ pub ES: u32,
+ pub INT_LOW: u32,
+ pub INT_HIGH: u32,
+ pub HRS_LOW: u32,
+ pub HRS_HIGH: u32,
+ _reserved0: [u8; 0x100 - 0x18],
+ pub GRPRI: [u32; 64],
+ _reserved1: [u8; 0x1_0000 - 0x200],
+ pub TCD: [tcd::RegisterBlock; 64],
+ }
+
+ const _: () = assert!(core::mem::offset_of!(RegisterBlock, GRPRI) == 0x100);
+ const _: () = assert!(core::mem::offset_of!(RegisterBlock, TCD) == 0x1_0000);
+
+ pub use super::edma3::CSR;
+ pub use super::edma3::{HRS as HRS_LOW, HRS as HRS_HIGH};
+}
diff --git a/drivers/edma/src/dmamux.rs b/drivers/edma/src/dmamux.rs
new file mode 100644
index 0000000..79f63e0
--- /dev/null
+++ b/drivers/edma/src/dmamux.rs
@@ -0,0 +1,16 @@
+//! DMA multiplexer.
+
+#[repr(C)]
+#[allow(non_snake_case)]
+pub struct RegisterBlock {
+ pub CHCFG: [u32; 32],
+}
+
+ral_registers::register! {
+ pub CHCFG<u32> RW [
+ ENBL start(31) width(1) RW {}
+ TRIG start(30) width(1) RW {}
+ A_ON start(29) width(1) RW {}
+ SOURCE start(0) width(8) RW {}
+ ]
+}
diff --git a/drivers/edma/src/lib.rs b/drivers/edma/src/lib.rs
new file mode 100644
index 0000000..7a23953
--- /dev/null
+++ b/drivers/edma/src/lib.rs
@@ -0,0 +1,990 @@
+//! DMA blocks for i.MX RT MCUs.
+//!
+//! eDMA works for all i.MX RT 1000 and 1100 series MCUs.
+//! The eDMA3 and eDMA4 is specifically targeted for the
+//! 1180 MCUs.
+
+#![no_std]
+
+pub mod dma;
+pub mod dmamux;
+pub mod tcd;
+
+pub mod element {
+ /// An ID for an element size.
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[non_exhaustive]
+ #[repr(u8)]
+ pub enum ElementSize {
+ /// `u8` transfer.
+ U8 = 0,
+ /// `u16` transfer.
+ U16 = 1,
+ /// `u32` transfer.
+ U32 = 2,
+ /// `u64` transfer.
+ U64 = 3,
+ }
+
+ /// Describes a transferrable DMA element; basically, an unsigned
+ /// integer of any size.
+ pub trait Element: Copy + private::Sealed {
+ /// An identifier describing the data transfer size
+ ///
+ /// Part of the TCD API; see documentation on TCD\[SSIZE\]
+ /// and TCD\[DSIZE\] for more information.
+ const DATA_TRANSFER_ID: ElementSize;
+ }
+
+ impl Element for u8 {
+ const DATA_TRANSFER_ID: ElementSize = ElementSize::U8;
+ }
+
+ impl Element for u16 {
+ const DATA_TRANSFER_ID: ElementSize = ElementSize::U16;
+ }
+
+ impl Element for u32 {
+ const DATA_TRANSFER_ID: ElementSize = ElementSize::U32;
+ }
+
+ impl Element for u64 {
+ const DATA_TRANSFER_ID: ElementSize = ElementSize::U64;
+ }
+
+ mod private {
+ pub trait Sealed {}
+
+ impl Sealed for u8 {}
+ impl Sealed for u16 {}
+ impl Sealed for u32 {}
+ impl Sealed for u64 {}
+ }
+}
+
+pub mod edma {
+ use core::num::NonZeroU8;
+
+ use crate::{dma::edma as dma, dmamux, element::ElementSize, tcd::edma as tcd};
+ use ral_registers::Instance;
+
+ /// A DMA channel for an eDMA controller.
+ ///
+ /// The channel is three pointers wide.
+ pub struct Channel {
+ index: usize,
+ dma: Instance<dma::RegisterBlock>,
+ mux: Instance<dmamux::RegisterBlock>,
+ }
+
+ impl Channel {
+ /// Create a new DMA channel.
+ ///
+ /// # Safety
+ ///
+ /// The channel formed by this call cannot alias another channel with the
+ /// same index. The channel index must be valid for the hardware.
+ pub const unsafe fn new(
+ dma: Instance<dma::RegisterBlock>,
+ mux: Instance<dmamux::RegisterBlock>,
+ index: usize,
+ ) -> Self {
+ Self { index, dma, mux }
+ }
+
+ /// Returns the channel index.
+ pub fn index(&self) -> usize {
+ self.index
+ }
+
+ /// Enable the DMA channel.
+ ///
+ /// # Safety
+ ///
+ /// If the channel is incorrectly configured, it may access
+ /// invalid memory.
+ pub unsafe fn enable(&mut self) {
+ ral_registers::write_reg!(dma, self.dma, SERQ, self.index as u8);
+ }
+
+ /// Disable the DMA channel.
+ pub fn disable(&mut self) {
+ ral_registers::write_reg!(dma, self.dma, CERQ, self.index as u8);
+ }
+
+ /// Returns `true` if the DMA channel is enabled.
+ pub fn is_enabled(&self) -> bool {
+ (1 << self.index) & ral_registers::read_reg!(dma, self.dma, ERQ) != 0
+ }
+
+ fn tcd(&self) -> Instance<tcd::RegisterBlock> {
+ // Safety: caller claims the index is valid for
+ // the DMA controller.
+ unsafe { Instance::new_unchecked(&raw mut (*self.dma.as_ptr()).TCD[self.index]) }
+ }
+
+ /// Returns `true` if the hardware is signaling requests towards
+ /// this DMA channel.
+ pub fn is_hardware_signaling(&self) -> bool {
+ (1 << self.index) & ral_registers::read_reg!(dma, self.dma, HRS) != 0
+ }
+
+ /// Returns `true` if an interrupt has activated for this
+ /// DMA channel.
+ pub fn is_interrupt(&self) -> bool {
+ (1 << self.index) & ral_registers::read_reg!(dma, self.dma, INT) != 0
+ }
+
+ /// Clear the interrupt trigger.
+ pub fn clear_interrupt(&self) {
+ ral_registers::write_reg!(dma, self.dma, CINT, self.index as u8);
+ }
+
+ /// Returns `true` if this DMA channel has finished its transfer.
+ pub fn is_done(&self) -> bool {
+ ral_registers::read_reg!(tcd, self.tcd(), CSR, DONE == 1)
+ }
+
+ /// Clear the "done" signal from software.
+ pub fn clear_done(&self) {
+ ral_registers::write_reg!(dma, self.dma, CDNE, self.index as u8);
+ }
+
+ /// Returns `true` if this channel has an error.
+ pub fn has_error(&self) -> bool {
+ (1 << self.index) & ral_registers::read_reg!(dma, self.dma, ERR) != 0
+ }
+
+ /// Clear this channel's error.
+ pub fn clear_error(&self) {
+ ral_registers::write_reg!(dma, self.dma, CERR, self.index as u8);
+ }
+
+ /// Returns `true` if this channel is actively transferring.
+ pub fn is_active(&self) -> bool {
+ ral_registers::read_reg!(tcd, self.tcd(), CSR, ACTIVE == 1)
+ }
+
+ /// Reset the control descriptor for the channel.
+ ///
+ /// Call this at least once, after acquiring the channel, to
+ /// put it into a known-good state.
+ pub fn reset(&mut self) {
+ unsafe { self.tcd().as_ptr().write_bytes(0, 1) };
+ }
+
+ /// Set the source address for a DMA transfer
+ ///
+ /// `saddr` should be a memory location that can provide the DMA controller
+ /// with data.
+ ///
+ /// # Safety
+ ///
+ /// If the DMA channel is already enabled, the DMA engine may start reading this
+ /// memory location. You must ensure that reads to `saddr` do not perform
+ /// inappropriate side effects. You must ensure `saddr` is valid for the
+ /// lifetime of the transfer.
+ pub unsafe fn set_source_address(&self, saddr: *const ()) {
+ ral_registers::write_reg!(tcd, self.tcd(), SADDR, saddr as u32);
+ }
+
+ /// Set the source offset *in bytes*
+ ///
+ /// `offset` could be negative, which would decrement the address.
+ ///
+ /// # Safety
+ ///
+ /// This method could allow a DMA engine to read beyond a buffer or
+ /// address. You must ensure that the source is valid for these offsets.
+ pub unsafe fn set_source_offset(&self, offset: i16) {
+ ral_registers::write_reg!(tcd, self.tcd(), SOFF, offset);
+ }
+
+ /// Set the destination address for a DMA transfer
+ ///
+ /// `daddr` should be a memory location that can store data from the
+ /// DMA controller.
+ ///
+ /// # Safety
+ ///
+ /// If the DMA channel is already enabled, the DMA engine may start
+ /// writing to this address. You must ensure that writes to `daddr`
+ /// are safe, and that the memory is valid for the lifetime of the
+ /// transfer.
+ pub unsafe fn set_destination_address(&self, daddr: *const ()) {
+ ral_registers::write_reg!(tcd, self.tcd(), DADDR, daddr as u32);
+ }
+
+ /// Set the destination offset *in bytes*
+ ///
+ /// `offset` could be negative, which would decrement the address.
+ ///
+ /// # Safety
+ ///
+ /// This method could allow a DMA engine to write beyond the range of
+ /// a buffer. You must ensure that the destination is valid for these
+ /// offsets.
+ pub unsafe fn set_destination_offset(&self, offset: i16) {
+ ral_registers::write_reg!(tcd, self.tcd(), DOFF, offset);
+ }
+
+ /// Set the transfer attributes.
+ ///
+ /// The attributes describes the ways the source is read and
+ /// the destination is written. The modulo mask allows the
+ /// buffer to be treated as a circular buffer.
+ ///
+ /// # Safety
+ ///
+ /// Incorrect sizes and modulos may cause invalid memory accesses.
+ pub unsafe fn set_attributes(
+ &self,
+ source_size: ElementSize,
+ source_modulo: u8,
+ destination_size: ElementSize,
+ destination_modulo: u8,
+ ) {
+ ral_registers::write_reg!(tcd, self.tcd(), ATTR,
+ SSIZE: source_size as u16,
+ SMOD: source_modulo as u16,
+ DSIZE: destination_size as u16,
+ DMOD: destination_modulo as u16,
+ );
+ }
+
+ /// Set the source last address adjustment *in bytes*
+ ///
+ /// # Safety
+ ///
+ /// This could allow the DMA engine to reference an invalid source buffer.
+ /// You must ensure that the adjustment performed by the DMA engine is
+ /// valid, assuming that another DMA transfer immediately runs after the
+ /// current transfer completes.
+ pub unsafe fn set_source_last_address_adjustment(&self, adjustment: i32) {
+ ral_registers::write_reg!(tcd, self.tcd(), SLAST, adjustment);
+ }
+
+ /// Set the destination last addrss adjustment *in bytes*
+ ///
+ /// # Safety
+ ///
+ /// This could allow the DMA engine to reference an invalid destination address.
+ /// You must ensure that the adjustment performed by the DMA engine is
+ /// valid, assuming that another DMA transfer immediately runs after the
+ /// current transfer completes.
+ pub unsafe fn set_destination_last_address_adjustment(&self, adjustment: i32) {
+ ral_registers::write_reg!(tcd, self.tcd(), DLAST_SGA, adjustment);
+ }
+
+ /// Set the number of *bytes* to transfer per minor loop
+ ///
+ /// Describes how many bytes we should transfer for each DMA service request.
+ /// Note that `nbytes` of `0` is interpreted as a 4GB transfer.
+ ///
+ /// # Safety
+ ///
+ /// This might allow the DMA engine to read beyond the source, or write beyond
+ /// the destination. Caller must ensure that the number of bytes per minor loop
+ /// is valid for the given transfer.
+ pub unsafe fn set_minor_loop_bytes(&self, nbytes: u32) {
+ ral_registers::write_reg!(tcd, self.tcd(), NBYTES, nbytes);
+ }
+
+ /// Tells the DMA channel how many transfer iterations to perform
+ ///
+ /// A 'transfer iteration' is a read from a source, and a write to a destination, with
+ /// read and write sizes described by a minor loop. Each iteration requires a DMA
+ /// service request, either from hardware or from software. The maximum number of iterations
+ /// is 2^15.
+ ///
+ /// # Safety
+ ///
+ /// This may allow the DMA engine to read beyond the source, or write beyond
+ /// the destination. Caller must ensure that the number of iterations is valid
+ /// for the transfer.
+ pub unsafe fn set_transfer_iterations(&mut self, iterations: u16) {
+ let iterations = iterations & 0x7FFF;
+ // Note that this is clearing the ELINK bit. We don't have support
+ // for channel-to-channel linking right now. Clearing ELINK is intentional
+ // to use the whole 15 bits for iterations.
+ ral_registers::write_reg!(tcd, self.tcd(), CITER, iterations);
+ ral_registers::write_reg!(tcd, self.tcd(), BITER, iterations);
+ }
+
+ /// Returns the beginning transfer iterations setting for the channel.
+ ///
+ /// This reflects the last call to `set_transfer_iterations`.
+ pub fn beginning_transfer_iterations(&self) -> u16 {
+ ral_registers::read_reg!(tcd, self.tcd(), BITER)
+ }
+
+ /// Enable or disable 'disable on completion'
+ ///
+ /// 'Disable on completion' lets the DMA channel automatically clear the request signal
+ /// when it completes a transfer.
+ pub fn set_disable_on_completion(&mut self, dreq: bool) {
+ ral_registers::modify_reg!(tcd, self.tcd(), CSR, DREQ: dreq as u16);
+ }
+
+ /// Enable or disable interrupt generation when the transfer completes
+ ///
+ /// You're responsible for registering your interrupt handler.
+ pub fn set_interrupt_on_completion(&mut self, intr: bool) {
+ ral_registers::modify_reg!(tcd, self.tcd(), CSR, INTMAJOR: intr as u16);
+ }
+
+ /// Start a DMA transfer
+ ///
+ /// `start()` should be used to request service from the DMA controller. It's
+ /// necessary for in-memory DMA transfers. Do not use it for hardware-initiated
+ /// DMA transfers. DMA transfers that involve hardware will rely on the hardware
+ /// to request DMA service.
+ ///
+ /// Flag is automatically cleared by hardware after it's asserted.
+ pub fn start(&mut self) {
+ ral_registers::modify_reg!(tcd, self.tcd(), CSR, START: 1);
+ }
+
+ /// Set the source signal for channel activation.
+ ///
+ /// The source is typically an upstream peripheral.
+ /// Use `None` to clear the source.
+ ///
+ /// This call reads back the written source. If they're equal
+ /// the return is `true`. The values may not be equal if the
+ /// source is selected in another DMA channel.
+ pub fn set_mux_source_signal(&self, signal: Option<NonZeroU8>) -> bool {
+ let signal = signal.map_or(0, NonZeroU8::get) as u32;
+ ral_registers::modify_reg!(dmamux, self.mux, CHCFG[self.index], SOURCE: signal);
+ signal == ral_registers::read_reg!(dmamux, self.mux, CHCFG[self.index], SOURCE)
+ }
+
+ /// Enable or disable the DMA MUX source signaling / triggering.
+ ///
+ /// Make sure to set up a source signal before enabling.
+ pub fn set_mux_source_enable(&self, enable: bool) {
+ ral_registers::modify_reg!(dmamux, self.mux, CHCFG[self.index], ENBL: enable as u32);
+ }
+
+ /// Set periodic triggering for this DMA channel.
+ ///
+ /// Only the first four DMA channels can periodically trigger
+ /// on a PIT timer. Note that this isn't enabled.
+ ///
+ /// # Panics
+ ///
+ /// Panics if this isn't one of the first four DMA channels.
+ pub fn set_mux_periodic(&self, periodic: bool) {
+ assert!(self.index < 4);
+ ral_registers::modify_reg!(dmamux, self.mux, CHCFG[self.index], TRIG: periodic as u32);
+ }
+ }
+}
+
+pub mod edma3 {
+ use core::num::NonZeroU8;
+
+ use crate::{dma::edma3 as dma, element::ElementSize, tcd::edma34 as tcd};
+ use ral_registers::Instance;
+
+ /// A DMA channel for an eDMA controller.
+ ///
+ /// The channel is two pointers wide.
+ pub struct Channel {
+ index: usize,
+ dma: Instance<dma::RegisterBlock>,
+ }
+
+ impl Channel {
+ /// Create a new DMA channel.
+ ///
+ /// # Safety
+ ///
+ /// The channel formed by this call cannot alias another channel with the
+ /// same index. The channel index must be valid for the hardware.
+ pub const unsafe fn new(dma: Instance<dma::RegisterBlock>, index: usize) -> Self {
+ Self { index, dma }
+ }
+
+ /// Enable the DMA channel.
+ ///
+ /// # Safety
+ ///
+ /// If the channel is incorrectly configured, it may access
+ /// invalid memory.
+ pub unsafe fn enable(&mut self) {
+ ral_registers::modify_reg!(tcd, self.tcd(), CSR, ERQ: 1, DONE: 0);
+ }
+
+ /// Disable the DMA channel.
+ pub fn disable(&mut self) {
+ ral_registers::modify_reg!(tcd, self.tcd(), CSR, ERQ: 0, DONE: 0);
+ }
+
+ /// Returns `true` if the DMA channel is enabled.
+ pub fn is_enabled(&self) -> bool {
+ ral_registers::read_reg!(tcd, self.tcd(), CSR, ERQ != 0)
+ }
+
+ fn tcd(&self) -> Instance<tcd::RegisterBlock> {
+ // Safety: caller claims the index is valid for
+ // the DMA controller.
+ unsafe { Instance::new_unchecked(&raw mut (*self.dma.as_ptr()).TCD[self.index]) }
+ }
+
+ /// Returns `true` if the hardware is signaling requests towards
+ /// this DMA channel.
+ pub fn is_hardware_signaling(&self) -> bool {
+ (1 << self.index) & ral_registers::read_reg!(dma, self.dma, HRS) != 0
+ }
+
+ /// Returns `true` if an interrupt has activated for this
+ /// DMA channel.
+ pub fn is_interrupt(&self) -> bool {
+ ral_registers::read_reg!(tcd, self.tcd(), INT) != 0
+ }
+
+ /// Clear the interrupt trigger.
+ pub fn clear_interrupt(&self) {
+ ral_registers::write_reg!(tcd, self.tcd(), INT, 1);
+ }
+
+ /// Returns `true` if this DMA channel has finished its transfer.
+ pub fn is_done(&self) -> bool {
+ ral_registers::read_reg!(tcd, self.tcd(), CSR, DONE == 1)
+ }
+
+ /// Clear the "done" signal from software.
+ pub fn clear_done(&self) {
+ // Safety: the CSR register is the first register of the
+ // TCD. The peripheral tolerates 8-bit access. We can poke
+ // the DONE bit with a single write without risking a race
+ // in the lower bits of the register.
+ //
+ // I should rewrite the register block to lay out the data
+ // like I want...
+ unsafe {
+ let csr: *mut u32 = &raw mut (*self.tcd().as_ptr()).CSR;
+ csr.cast::<u8>().add(3).write_volatile(1 << 6)
+ };
+ }
+
+ /// Returns `true` if this channel has an error.
+ pub fn has_error(&self) -> bool {
+ ral_registers::read_reg!(tcd, self.tcd(), ES) != 0
+ }
+
+ /// Clear this channel's error.
+ pub fn clear_error(&self) {
+ ral_registers::write_reg!(tcd, self.tcd(), ES, ERR: 1);
+ }
+
+ /// Returns `true` if this channel is actively transferring.
+ pub fn is_active(&self) -> bool {
+ ral_registers::read_reg!(tcd, self.tcd(), CSR, ACTIVE == 1)
+ }
+
+ /// Reset the control descriptor for the channel.
+ ///
+ /// Call this at least once, after acquiring the channel, to
+ /// put it into a known-good state.
+ pub fn reset(&mut self) {
+ unsafe {
+ let tcd = &raw mut (*self.tcd().as_ptr()).TCD;
+ tcd.write_bytes(0, 1);
+ };
+ }
+
+ /// Set the source address for a DMA transfer
+ ///
+ /// `saddr` should be a memory location that can provide the DMA controller
+ /// with data.
+ ///
+ /// # Safety
+ ///
+ /// If the DMA channel is already enabled, the DMA engine may start reading this
+ /// memory location. You must ensure that reads to `saddr` do not perform
+ /// inappropriate side effects. You must ensure `saddr` is valid for the
+ /// lifetime of the transfer.
+ pub unsafe fn set_source_address(&self, saddr: *const ()) {
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.SADDR, saddr as u32);
+ }
+
+ /// Set the source offset *in bytes*
+ ///
+ /// `offset` could be negative, which would decrement the address.
+ ///
+ /// # Safety
+ ///
+ /// This method could allow a DMA engine to read beyond a buffer or
+ /// address. You must ensure that the source is valid for these offsets.
+ pub unsafe fn set_source_offset(&self, offset: i16) {
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.SOFF, offset);
+ }
+
+ /// Set the destination address for a DMA transfer
+ ///
+ /// `daddr` should be a memory location that can store data from the
+ /// DMA controller.
+ ///
+ /// # Safety
+ ///
+ /// If the DMA channel is already enabled, the DMA engine may start
+ /// writing to this address. You must ensure that writes to `daddr`
+ /// are safe, and that the memory is valid for the lifetime of the
+ /// transfer.
+ pub unsafe fn set_destination_address(&self, daddr: *const ()) {
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.DADDR, daddr as u32);
+ }
+
+ /// Set the destination offset *in bytes*
+ ///
+ /// `offset` could be negative, which would decrement the address.
+ ///
+ /// # Safety
+ ///
+ /// This method could allow a DMA engine to write beyond the range of
+ /// a buffer. You must ensure that the destination is valid for these
+ /// offsets.
+ pub unsafe fn set_destination_offset(&self, offset: i16) {
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.DOFF, offset);
+ }
+
+ /// Set the transfer attributes.
+ ///
+ /// The attributes describes the ways the source is read and
+ /// the destination is written. The modulo mask allows the
+ /// buffer to be treated as a circular buffer.
+ ///
+ /// # Safety
+ ///
+ /// Incorrect sizes and modulos may cause invalid memory accesses.
+ pub unsafe fn set_attributes(
+ &self,
+ source_size: ElementSize,
+ source_modulo: u8,
+ destination_size: ElementSize,
+ destination_modulo: u8,
+ ) {
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.ATTR,
+ SSIZE: source_size as u16,
+ SMOD: source_modulo as u16,
+ DSIZE: destination_size as u16,
+ DMOD: destination_modulo as u16,
+ );
+ }
+
+ /// Set the source last address adjustment *in bytes*
+ ///
+ /// # Safety
+ ///
+ /// This could allow the DMA engine to reference an invalid source buffer.
+ /// You must ensure that the adjustment performed by the DMA engine is
+ /// valid, assuming that another DMA transfer immediately runs after the
+ /// current transfer completes.
+ pub unsafe fn set_source_last_address_adjustment(&self, adjustment: i32) {
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.SLAST, adjustment);
+ }
+
+ /// Set the destination last addrss adjustment *in bytes*
+ ///
+ /// # Safety
+ ///
+ /// This could allow the DMA engine to reference an invalid destination address.
+ /// You must ensure that the adjustment performed by the DMA engine is
+ /// valid, assuming that another DMA transfer immediately runs after the
+ /// current transfer completes.
+ pub unsafe fn set_destination_last_address_adjustment(&self, adjustment: i32) {
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.DLAST_SGA, adjustment);
+ }
+
+ /// Set the number of *bytes* to transfer per minor loop
+ ///
+ /// Describes how many bytes we should transfer for each DMA service request.
+ /// Note that `nbytes` of `0` is interpreted as a 4GB transfer.
+ ///
+ /// # Safety
+ ///
+ /// This might allow the DMA engine to read beyond the source, or write beyond
+ /// the destination. Caller must ensure that the number of bytes per minor loop
+ /// is valid for the given transfer.
+ pub unsafe fn set_minor_loop_bytes(&self, nbytes: u32) {
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.NBYTES, nbytes);
+ }
+
+ /// Tells the DMA channel how many transfer iterations to perform
+ ///
+ /// A 'transfer iteration' is a read from a source, and a write to a destination, with
+ /// read and write sizes described by a minor loop. Each iteration requires a DMA
+ /// service request, either from hardware or from software. The maximum number of iterations
+ /// is 2^15.
+ ///
+ /// # Safety
+ ///
+ /// This may allow the DMA engine to read beyond the source, or write beyond
+ /// the destination. Caller must ensure that the number of iterations is valid
+ /// for the transfer.
+ pub unsafe fn set_transfer_iterations(&mut self, iterations: u16) {
+ let iterations = iterations & 0x7FFF;
+ // Note that this is clearing the ELINK bit. We don't have support
+ // for channel-to-channel linking right now. Clearing ELINK is intentional
+ // to use the whole 15 bits for iterations.
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.CITER, iterations);
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.BITER, iterations);
+ }
+
+ /// Returns the beginning transfer iterations setting for the channel.
+ ///
+ /// This reflects the last call to `set_transfer_iterations`.
+ pub fn beginning_transfer_iterations(&self) -> u16 {
+ ral_registers::read_reg!(tcd, self.tcd(), TCD.BITER)
+ }
+
+ /// Enable or disable 'disable on completion'
+ ///
+ /// 'Disable on completion' lets the DMA channel automatically clear the request signal
+ /// when it completes a transfer.
+ pub fn set_disable_on_completion(&mut self, dreq: bool) {
+ ral_registers::modify_reg!(tcd, self.tcd(), TCD.CSR, DREQ: dreq as u16);
+ }
+
+ /// Enable or disable interrupt generation when the transfer completes
+ ///
+ /// You're responsible for registering your interrupt handler.
+ pub fn set_interrupt_on_completion(&mut self, intr: bool) {
+ ral_registers::modify_reg!(tcd, self.tcd(), TCD.CSR, INTMAJOR: intr as u16);
+ }
+
+ /// Start a DMA transfer
+ ///
+ /// `start()` should be used to request service from the DMA controller. It's
+ /// necessary for in-memory DMA transfers. Do not use it for hardware-initiated
+ /// DMA transfers. DMA transfers that involve hardware will rely on the hardware
+ /// to request DMA service.
+ ///
+ /// Flag is automatically cleared by hardware after it's asserted.
+ pub fn start(&mut self) {
+ ral_registers::modify_reg!(tcd, self.tcd(), TCD.CSR, START: 1);
+ }
+
+ /// Set the source signal for channel activation.
+ ///
+ /// The source is typically an upstream peripheral.
+ /// Use `None` to disable the source.
+ ///
+ /// This call reads back the written source. If they're equal
+ /// the return is `true`. The values may not be equal if the
+ /// source is selected in another DMA channel.
+ pub fn set_source_signal(&self, signal: Option<NonZeroU8>) -> bool {
+ let signal = signal.map_or(0, NonZeroU8::get) as u32;
+ ral_registers::write_reg!(tcd, self.tcd(), MUX, SRC: signal);
+ signal == ral_registers::read_reg!(tcd, self.tcd(), MUX, SRC)
+ }
+ }
+}
+
+pub mod edma4 {
+ use core::num::NonZeroU8;
+
+ use crate::{dma::edma4 as dma, element::ElementSize, tcd::edma34 as tcd};
+ use ral_registers::Instance;
+
+ /// A DMA channel for an eDMA controller.
+ ///
+ /// The channel is two pointers wide.
+ pub struct Channel {
+ index: usize,
+ dma: Instance<dma::RegisterBlock>,
+ }
+
+ impl Channel {
+ /// Create a new DMA channel.
+ ///
+ /// # Safety
+ ///
+ /// The channel formed by this call cannot alias another channel with the
+ /// same index. The channel index must be valid for the hardware.
+ pub const unsafe fn new(dma: Instance<dma::RegisterBlock>, index: usize) -> Self {
+ Self { index, dma }
+ }
+
+ /// Enable the DMA channel.
+ ///
+ /// # Safety
+ ///
+ /// If the channel is incorrectly configured, it may access
+ /// invalid memory.
+ pub unsafe fn enable(&mut self) {
+ ral_registers::modify_reg!(tcd, self.tcd(), CSR, ERQ: 1, DONE: 0);
+ }
+
+ /// Disable the DMA channel.
+ pub fn disable(&mut self) {
+ ral_registers::modify_reg!(tcd, self.tcd(), CSR, ERQ: 0, DONE: 0);
+ }
+
+ /// Returns `true` if the DMA channel is enabled.
+ pub fn is_enabled(&self) -> bool {
+ ral_registers::read_reg!(tcd, self.tcd(), CSR, ERQ != 0)
+ }
+
+ fn tcd(&self) -> Instance<tcd::RegisterBlock> {
+ // Safety: caller claims the index is valid for
+ // the DMA controller.
+ unsafe { Instance::new_unchecked(&raw mut (*self.dma.as_ptr()).TCD[self.index]) }
+ }
+
+ /// Returns `true` if the hardware is signaling requests towards
+ /// this DMA channel.
+ pub fn is_hardware_signaling(&self) -> bool {
+ if self.index < 32 {
+ (1 << self.index) & ral_registers::read_reg!(dma, self.dma, HRS_LOW) != 0
+ } else {
+ (1 << (self.index - 32)) & ral_registers::read_reg!(dma, self.dma, HRS_HIGH) != 0
+ }
+ }
+
+ /// Returns `true` if an interrupt has activated for this
+ /// DMA channel.
+ pub fn is_interrupt(&self) -> bool {
+ ral_registers::read_reg!(tcd, self.tcd(), INT) != 0
+ }
+
+ /// Clear the interrupt trigger.
+ pub fn clear_interrupt(&self) {
+ ral_registers::write_reg!(tcd, self.tcd(), INT, 1);
+ }
+
+ /// Returns `true` if this DMA channel has finished its transfer.
+ pub fn is_done(&self) -> bool {
+ ral_registers::read_reg!(tcd, self.tcd(), CSR, DONE == 1)
+ }
+
+ /// Clear the "done" signal from software.
+ pub fn clear_done(&self) {
+ // Safety: the CSR register is the first register of the
+ // TCD. The peripheral tolerates 8-bit access. We can poke
+ // the DONE bit with a single write without risking a race
+ // in the lower bits of the register.
+ //
+ // I should rewrite the register block to lay out the data
+ // like I want...
+ unsafe {
+ let csr: *mut u32 = &raw mut (*self.tcd().as_ptr()).CSR;
+ csr.cast::<u8>().add(3).write_volatile(1 << 6)
+ };
+ }
+
+ /// Returns `true` if this channel has an error.
+ pub fn has_error(&self) -> bool {
+ ral_registers::read_reg!(tcd, self.tcd(), ES) != 0
+ }
+
+ /// Clear this channel's error.
+ pub fn clear_error(&self) {
+ ral_registers::write_reg!(tcd, self.tcd(), ES, ERR: 1);
+ }
+
+ /// Returns `true` if this channel is actively transferring.
+ pub fn is_active(&self) -> bool {
+ ral_registers::read_reg!(tcd, self.tcd(), CSR, ACTIVE == 1)
+ }
+
+ /// Reset the control descriptor for the channel.
+ ///
+ /// Call this at least once, after acquiring the channel, to
+ /// put it into a known-good state.
+ pub fn reset(&mut self) {
+ unsafe {
+ let tcd = &raw mut (*self.tcd().as_ptr()).TCD;
+ tcd.write_bytes(0, 1);
+ };
+ }
+
+ /// Set the source address for a DMA transfer
+ ///
+ /// `saddr` should be a memory location that can provide the DMA controller
+ /// with data.
+ ///
+ /// # Safety
+ ///
+ /// If the DMA channel is already enabled, the DMA engine may start reading this
+ /// memory location. You must ensure that reads to `saddr` do not perform
+ /// inappropriate side effects. You must ensure `saddr` is valid for the
+ /// lifetime of the transfer.
+ pub unsafe fn set_source_address(&self, saddr: *const ()) {
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.SADDR, saddr as u32);
+ }
+
+ /// Set the source offset *in bytes*
+ ///
+ /// `offset` could be negative, which would decrement the address.
+ ///
+ /// # Safety
+ ///
+ /// This method could allow a DMA engine to read beyond a buffer or
+ /// address. You must ensure that the source is valid for these offsets.
+ pub unsafe fn set_source_offset(&self, offset: i16) {
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.SOFF, offset);
+ }
+
+ /// Set the destination address for a DMA transfer
+ ///
+ /// `daddr` should be a memory location that can store data from the
+ /// DMA controller.
+ ///
+ /// # Safety
+ ///
+ /// If the DMA channel is already enabled, the DMA engine may start
+ /// writing to this address. You must ensure that writes to `daddr`
+ /// are safe, and that the memory is valid for the lifetime of the
+ /// transfer.
+ pub unsafe fn set_destination_address(&self, daddr: *const ()) {
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.DADDR, daddr as u32);
+ }
+
+ /// Set the destination offset *in bytes*
+ ///
+ /// `offset` could be negative, which would decrement the address.
+ ///
+ /// # Safety
+ ///
+ /// This method could allow a DMA engine to write beyond the range of
+ /// a buffer. You must ensure that the destination is valid for these
+ /// offsets.
+ pub unsafe fn set_destination_offset(&self, offset: i16) {
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.DOFF, offset);
+ }
+
+ /// Set the transfer attributes.
+ ///
+ /// The attributes describes the ways the source is read and
+ /// the destination is written. The modulo mask allows the
+ /// buffer to be treated as a circular buffer.
+ ///
+ /// # Safety
+ ///
+ /// Incorrect sizes and modulos may cause invalid memory accesses.
+ pub unsafe fn set_attributes(
+ &self,
+ source_size: ElementSize,
+ source_modulo: u8,
+ destination_size: ElementSize,
+ destination_modulo: u8,
+ ) {
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.ATTR,
+ SSIZE: source_size as u16,
+ SMOD: source_modulo as u16,
+ DSIZE: destination_size as u16,
+ DMOD: destination_modulo as u16,
+ );
+ }
+
+ /// Set the source last address adjustment *in bytes*
+ ///
+ /// # Safety
+ ///
+ /// This could allow the DMA engine to reference an invalid source buffer.
+ /// You must ensure that the adjustment performed by the DMA engine is
+ /// valid, assuming that another DMA transfer immediately runs after the
+ /// current transfer completes.
+ pub unsafe fn set_source_last_address_adjustment(&self, adjustment: i32) {
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.SLAST, adjustment);
+ }
+
+ /// Set the destination last addrss adjustment *in bytes*
+ ///
+ /// # Safety
+ ///
+ /// This could allow the DMA engine to reference an invalid destination address.
+ /// You must ensure that the adjustment performed by the DMA engine is
+ /// valid, assuming that another DMA transfer immediately runs after the
+ /// current transfer completes.
+ pub unsafe fn set_destination_last_address_adjustment(&self, adjustment: i32) {
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.DLAST_SGA, adjustment);
+ }
+
+ /// Set the number of *bytes* to transfer per minor loop
+ ///
+ /// Describes how many bytes we should transfer for each DMA service request.
+ /// Note that `nbytes` of `0` is interpreted as a 4GB transfer.
+ ///
+ /// # Safety
+ ///
+ /// This might allow the DMA engine to read beyond the source, or write beyond
+ /// the destination. Caller must ensure that the number of bytes per minor loop
+ /// is valid for the given transfer.
+ pub unsafe fn set_minor_loop_bytes(&self, nbytes: u32) {
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.NBYTES, nbytes);
+ }
+
+ /// Tells the DMA channel how many transfer iterations to perform
+ ///
+ /// A 'transfer iteration' is a read from a source, and a write to a destination, with
+ /// read and write sizes described by a minor loop. Each iteration requires a DMA
+ /// service request, either from hardware or from software. The maximum number of iterations
+ /// is 2^15.
+ ///
+ /// # Safety
+ ///
+ /// This may allow the DMA engine to read beyond the source, or write beyond
+ /// the destination. Caller must ensure that the number of iterations is valid
+ /// for the transfer.
+ pub unsafe fn set_transfer_iterations(&mut self, iterations: u16) {
+ let iterations = iterations & 0x7FFF;
+ // Note that this is clearing the ELINK bit. We don't have support
+ // for channel-to-channel linking right now. Clearing ELINK is intentional
+ // to use the whole 15 bits for iterations.
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.CITER, iterations);
+ ral_registers::write_reg!(tcd, self.tcd(), TCD.BITER, iterations);
+ }
+
+ /// Returns the beginning transfer iterations setting for the channel.
+ ///
+ /// This reflects the last call to `set_transfer_iterations`.
+ pub fn beginning_transfer_iterations(&self) -> u16 {
+ ral_registers::read_reg!(tcd, self.tcd(), TCD.BITER)
+ }
+
+ /// Enable or disable 'disable on completion'
+ ///
+ /// 'Disable on completion' lets the DMA channel automatically clear the request signal
+ /// when it completes a transfer.
+ pub fn set_disable_on_completion(&mut self, dreq: bool) {
+ ral_registers::modify_reg!(tcd, self.tcd(), TCD.CSR, DREQ: dreq as u16);
+ }
+
+ /// Enable or disable interrupt generation when the transfer completes
+ ///
+ /// You're responsible for registering your interrupt handler.
+ pub fn set_interrupt_on_completion(&mut self, intr: bool) {
+ ral_registers::modify_reg!(tcd, self.tcd(), TCD.CSR, INTMAJOR: intr as u16);
+ }
+
+ /// Start a DMA transfer
+ ///
+ /// `start()` should be used to request service from the DMA controller. It's
+ /// necessary for in-memory DMA transfers. Do not use it for hardware-initiated
+ /// DMA transfers. DMA transfers that involve hardware will rely on the hardware
+ /// to request DMA service.
+ ///
+ /// Flag is automatically cleared by hardware after it's asserted.
+ pub fn start(&mut self) {
+ ral_registers::modify_reg!(tcd, self.tcd(), TCD.CSR, START: 1);
+ }
+
+ /// Set the source signal for channel activation.
+ ///
+ /// The source is typically an upstream peripheral.
+ /// Use `None` to disable the source.
+ ///
+ /// This call reads back the written source. If they're equal
+ /// the return is `true`. The values may not be equal if the
+ /// source is selected in another DMA channel.
+ pub fn set_source_signal(&self, signal: Option<NonZeroU8>) -> bool {
+ let signal = signal.map_or(0, NonZeroU8::get) as u32;
+ ral_registers::write_reg!(tcd, self.tcd(), MUX, SRC: signal);
+ signal == ral_registers::read_reg!(tcd, self.tcd(), MUX, SRC)
+ }
+ }
+}
diff --git a/drivers/edma/src/tcd.rs b/drivers/edma/src/tcd.rs
new file mode 100644
index 0000000..1e76533
--- /dev/null
+++ b/drivers/edma/src/tcd.rs
@@ -0,0 +1,174 @@
+//! Transfer control descriptor.
+
+/// Common TCD layout for all eDMA implementations.
+///
+/// Fields may vary. See inline notes.
+mod common {
+ #[repr(C, align(32))]
+ #[allow(non_snake_case)]
+ pub struct RegisterBlock {
+ pub SADDR: u32,
+ // Signed numbers for offsets / 'last' members intentional.
+ // The hardware treats them as signed numbers.
+ pub SOFF: i16,
+ pub ATTR: u16,
+ pub NBYTES: u32,
+ pub SLAST: i32,
+ pub DADDR: u32,
+ pub DOFF: i16,
+ /// The minor loop channel link field may vary in size
+ /// depending on the implementation. Not worried right
+ /// now, since we don't support minor loop linking.
+ pub CITER: u16,
+ pub DLAST_SGA: i32,
+ /// These fields vary across all of eDMA, eDMA3 and
+ /// eDMA4!
+ ///
+ /// Major loop channel linking field size changes as
+ /// a function of the number of DMA channels.
+ ///
+ /// eDMA and eDMA3 have bandwidth control. eDMA4 has
+ /// transfer mode control for read-only / write-only
+ /// DMA transfers. Field is the same size.
+ ///
+ /// In the low byte, high nibble, eDMA has DONE and
+ /// ACTIVE. eDMA3 and eDMA4 have things we probably don't
+ /// need. Low byte, low nibble is the same.
+ ///
+ /// So we'll need to change how we handle DONE and
+ /// ACTIVE access. They can't always dispatch to this
+ /// register.
+ pub CSR: u16,
+ /// See CITER documentation note about eDMA3 bitfield
+ /// size when minor loop channel linking is enabled.
+ pub BITER: u16,
+ }
+
+ const _: () = assert!(32 == size_of::<RegisterBlock>());
+
+ ral_registers::register! {
+ pub SADDR<u32> RW []
+ }
+
+ ral_registers::register! {
+ pub SOFF<i16> RW []
+ }
+
+ ral_registers::register! {
+ pub ATTR<u16> RW [
+ #[doc = "Destination data transfer size"]
+ DSIZE start(0) width(3) RW {}
+ #[doc = "Destination Address Modulo"]
+ DMOD start(3) width(5) RW {}
+ #[doc = "Source data transfer size"]
+ SSIZE start(8) width(3) RW {}
+ #[doc = "Source Address Modulo"]
+ SMOD start(11) width(5) RW {}
+ ]
+ }
+
+ ral_registers::register! {
+ pub NBYTES<u32> RW []
+ }
+
+ ral_registers::register! {
+ pub SLAST<i32> RW []
+ }
+
+ ral_registers::register! {
+ pub DADDR<u32> RW []
+ }
+
+ ral_registers::register! {
+ pub DOFF<i16> RW []
+ }
+
+ ral_registers::register! {
+ pub CITER<u16> RW []
+ }
+
+ ral_registers::register! {
+ pub DLAST_SGA<i32> RW []
+ }
+
+ ral_registers::register! {
+ pub CSR<u16> RW [
+ START start(0) width(1) RW {}
+ INTMAJOR start(1) width(1) RW {}
+ DREQ start(3) width(1) RW {}
+ ACTIVE start(6) width(1) RW {}
+ DONE start(7) width(1) RW {}
+ ]
+ }
+
+ ral_registers::register! {
+ pub BITER<u16> RW []
+ }
+}
+
+pub mod edma {
+ pub use super::common::*;
+}
+
+pub mod edma34 {
+ #[repr(C, align(32))]
+ #[allow(non_snake_case)]
+ pub struct RegisterBlock {
+ pub CSR: u32,
+ pub ES: u32,
+ pub INT: u32,
+ pub SBR: u32,
+ pub PRI: u32,
+ pub MUX: u32,
+ /// Available on eDMA4, and reserved on eDMA3.
+ _mattr: u32,
+ pub TCD: super::common::RegisterBlock,
+ _reserved: [u8; 0x1_0000 - (4 * 8 + size_of::<super::common::RegisterBlock>())],
+ }
+
+ const _: () = assert!(core::mem::offset_of!(RegisterBlock, TCD) == 0x20);
+ const _: () = assert!(size_of::<RegisterBlock>() == 0x1_0000);
+
+ ral_registers::register! {
+ pub CSR<u32> RW [
+ ACTIVE start(31) width(1) RW {}
+ DONE start(30) width(1) RW {}
+ ERQ start(0) width(1) RW {}
+ ]
+ }
+
+ ral_registers::register! {
+ pub SBR<u32> RW [
+ EMI start(16) width(1) RW {}
+ PAL start(15) width(1) RW {}
+ SEC start(14) width(1) RW {}
+ ]
+ }
+
+ ral_registers::register! {
+ pub INT<u32> RW []
+ }
+
+ ral_registers::register! {
+ pub ES<u32> RW [
+ ERR start(31) width(1) RW {}
+ ]
+ }
+
+ ral_registers::register! {
+ pub MUX<u32> RW [
+ SRC start(0) width(6) RW {}
+ ]
+ }
+
+ #[allow(non_snake_case)]
+ pub mod TCD {
+ pub use crate::tcd::common::{
+ ATTR, BITER, CITER, DADDR, DLAST_SGA, DOFF, NBYTES, SADDR, SLAST, SOFF,
+ };
+
+ pub mod CSR {
+ pub use crate::tcd::common::CSR::{DREQ, INTMAJOR, START, access};
+ }
+ }
+}
diff --git a/drivers/enet/Cargo.toml b/drivers/enet/Cargo.toml
new file mode 100644
index 0000000..3c7d630
--- /dev/null
+++ b/drivers/enet/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "imxrt-drivers-enet"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+ral-registers = { workspace = true }
diff --git a/drivers/enet/src/lib.rs b/drivers/enet/src/lib.rs
new file mode 100644
index 0000000..409d252
--- /dev/null
+++ b/drivers/enet/src/lib.rs
@@ -0,0 +1,725 @@
+#![no_std]
+
+#[repr(C)]
+#[allow(non_snake_case)]
+pub struct RegisterBlock {
+ _reserved0: [u8; 0x04],
+ #[doc = "Interrupt Event Register"]
+ pub EIR: u32,
+ #[doc = "Interrupt Mask Register"]
+ pub EIMR: u32,
+ _reserved1: [u8; 0x04],
+ #[doc = "Receive Descriptor Active Register - Ring 0"]
+ pub RDAR: u32,
+ #[doc = "Transmit Descriptor Active Register - Ring 0"]
+ pub TDAR: u32,
+ _reserved2: [u8; 0x0c],
+ #[doc = "Ethernet Control Register"]
+ pub ECR: u32,
+ _reserved3: [u8; 0x18],
+ #[doc = "MII Management Frame Register"]
+ pub MMFR: u32,
+ #[doc = "MII Speed Control Register"]
+ pub MSCR: u32,
+ _reserved4: [u8; 0x1c],
+ #[doc = "MIB Control Register"]
+ pub MIBC: u32,
+ _reserved5: [u8; 0x1c],
+ #[doc = "Receive Control Register"]
+ pub RCR: u32,
+ _reserved6: [u8; 0x3c],
+ #[doc = "Transmit Control Register"]
+ pub TCR: u32,
+ _reserved7: [u8; 0x1c],
+ #[doc = "Physical Address Lower Register"]
+ pub PALR: u32,
+ #[doc = "Physical Address Upper Register"]
+ pub PAUR: u32,
+ #[doc = "Opcode/Pause Duration Register"]
+ pub OPD: u32,
+ #[doc = "Transmit Interrupt Coalescing Register"]
+ pub TXIC: [u32; 3usize],
+ _reserved8: [u8; 0x04],
+ #[doc = "Receive Interrupt Coalescing Register"]
+ pub RXIC: [u32; 3usize],
+ _reserved9: [u8; 0x0c],
+ #[doc = "Descriptor Individual Upper Address Register"]
+ pub IAUR: u32,
+ #[doc = "Descriptor Individual Lower Address Register"]
+ pub IALR: u32,
+ #[doc = "Descriptor Group Upper Address Register"]
+ pub GAUR: u32,
+ #[doc = "Descriptor Group Lower Address Register"]
+ pub GALR: u32,
+ _reserved10: [u8; 0x1c],
+ #[doc = "Transmit FIFO Watermark Register"]
+ pub TFWR: u32,
+ _reserved11: [u8; 0x18],
+ #[doc = "Receive Descriptor Ring 1 Start Register"]
+ pub RDSR1: u32,
+ #[doc = "Transmit Buffer Descriptor Ring 1 Start Register"]
+ pub TDSR1: u32,
+ #[doc = "Maximum Receive Buffer Size Register - Ring 1"]
+ pub MRBR1: u32,
+ #[doc = "Receive Descriptor Ring 2 Start Register"]
+ pub RDSR2: u32,
+ #[doc = "Transmit Buffer Descriptor Ring 2 Start Register"]
+ pub TDSR2: u32,
+ #[doc = "Maximum Receive Buffer Size Register - Ring 2"]
+ pub MRBR2: u32,
+ _reserved12: [u8; 0x08],
+ #[doc = "Receive Descriptor Ring 0 Start Register"]
+ pub RDSR: u32,
+ #[doc = "Transmit Buffer Descriptor Ring 0 Start Register"]
+ pub TDSR: u32,
+ #[doc = "Maximum Receive Buffer Size Register - Ring 0"]
+ pub MRBR: u32,
+ _reserved13: [u8; 0x04],
+ #[doc = "Receive FIFO Section Full Threshold"]
+ pub RSFL: u32,
+ #[doc = "Receive FIFO Section Empty Threshold"]
+ pub RSEM: u32,
+ #[doc = "Receive FIFO Almost Empty Threshold"]
+ pub RAEM: u32,
+ #[doc = "Receive FIFO Almost Full Threshold"]
+ pub RAFL: u32,
+ #[doc = "Transmit FIFO Section Empty Threshold"]
+ pub TSEM: u32,
+ #[doc = "Transmit FIFO Almost Empty Threshold"]
+ pub TAEM: u32,
+ #[doc = "Transmit FIFO Almost Full Threshold"]
+ pub TAFL: u32,
+ #[doc = "Transmit Inter-Packet Gap"]
+ pub TIPG: u32,
+ #[doc = "Frame Truncation Length"]
+ pub FTRL: u32,
+ _reserved14: [u8; 0x0c],
+ #[doc = "Transmit Accelerator Function Configuration"]
+ pub TACC: u32,
+ #[doc = "Receive Accelerator Function Configuration"]
+ pub RACC: u32,
+ #[doc = "Receive Classification Match Register for Class n"]
+ pub RCMR: [u32; 2usize],
+ _reserved15: [u8; 0x08],
+ #[doc = "DMA Class Based Configuration"]
+ pub DMACFG: [u32; 2usize],
+ #[doc = "Receive Descriptor Active Register - Ring 1"]
+ pub RDAR1: u32,
+ #[doc = "Transmit Descriptor Active Register - Ring 1"]
+ pub TDAR1: u32,
+ #[doc = "Receive Descriptor Active Register - Ring 2"]
+ pub RDAR2: u32,
+ #[doc = "Transmit Descriptor Active Register - Ring 2"]
+ pub TDAR2: u32,
+ #[doc = "QOS Scheme"]
+ pub QOS: u32,
+ _reserved16: [u8; 0x10],
+ #[doc = "Tx Packet Count Statistic Register"]
+ pub RMON_T_PACKETS: u32,
+ #[doc = "Tx Broadcast Packets Statistic Register"]
+ pub RMON_T_BC_PKT: u32,
+ #[doc = "Tx Multicast Packets Statistic Register"]
+ pub RMON_T_MC_PKT: u32,
+ #[doc = "Tx Packets with CRC/Align Error Statistic Register"]
+ pub RMON_T_CRC_ALIGN: u32,
+ #[doc = "Tx Packets Less Than Bytes and Good CRC Statistic Register"]
+ pub RMON_T_UNDERSIZE: u32,
+ #[doc = "Tx Packets GT MAX_FL bytes and Good CRC Statistic Register"]
+ pub RMON_T_OVERSIZE: u32,
+ #[doc = "Tx Packets Less Than 64 Bytes and Bad CRC Statistic Register"]
+ pub RMON_T_FRAG: u32,
+ #[doc = "Tx Packets Greater Than MAX_FL bytes and Bad CRC Statistic Register"]
+ pub RMON_T_JAB: u32,
+ #[doc = "Tx Collision Count Statistic Register"]
+ pub RMON_T_COL: u32,
+ #[doc = "Tx 64-Byte Packets Statistic Register"]
+ pub RMON_T_P64: u32,
+ #[doc = "Tx 65- to 127-byte Packets Statistic Register"]
+ pub RMON_T_P65TO127: u32,
+ #[doc = "Tx 128- to 255-byte Packets Statistic Register"]
+ pub RMON_T_P128TO255: u32,
+ #[doc = "Tx 256- to 511-byte Packets Statistic Register"]
+ pub RMON_T_P256TO511: u32,
+ #[doc = "Tx 512- to 1023-byte Packets Statistic Register"]
+ pub RMON_T_P512TO1023: u32,
+ #[doc = "Tx 1024- to 2047-byte Packets Statistic Register"]
+ pub RMON_T_P1024TO2047: u32,
+ #[doc = "Tx Packets Greater Than 2048 Bytes Statistic Register"]
+ pub RMON_T_P_GTE2048: u32,
+ #[doc = "Tx Octets Statistic Register"]
+ pub RMON_T_OCTETS: u32,
+ #[doc = "Reserved Statistic Register"]
+ pub IEEE_T_DROP: u32,
+ #[doc = "Frames Transmitted OK Statistic Register"]
+ pub IEEE_T_FRAME_OK: u32,
+ #[doc = "Frames Transmitted with Single Collision Statistic Register"]
+ pub IEEE_T_1COL: u32,
+ #[doc = "Frames Transmitted with Multiple Collisions Statistic Register"]
+ pub IEEE_T_MCOL: u32,
+ #[doc = "Frames Transmitted after Deferral Delay Statistic Register"]
+ pub IEEE_T_DEF: u32,
+ #[doc = "Frames Transmitted with Late Collision Statistic Register"]
+ pub IEEE_T_LCOL: u32,
+ #[doc = "Frames Transmitted with Excessive Collisions Statistic Register"]
+ pub IEEE_T_EXCOL: u32,
+ #[doc = "Frames Transmitted with Tx FIFO Underrun Statistic Register"]
+ pub IEEE_T_MACERR: u32,
+ #[doc = "Frames Transmitted with Carrier Sense Error Statistic Register"]
+ pub IEEE_T_CSERR: u32,
+ #[doc = "Reserved Statistic Register"]
+ pub IEEE_T_SQE: u32,
+ #[doc = "Flow Control Pause Frames Transmitted Statistic Register"]
+ pub IEEE_T_FDXFC: u32,
+ #[doc = "Octet Count for Frames Transmitted w/o Error Statistic Register"]
+ pub IEEE_T_OCTETS_OK: u32,
+ _reserved17: [u8; 0x0c],
+ #[doc = "Rx Packet Count Statistic Register"]
+ pub RMON_R_PACKETS: u32,
+ #[doc = "Rx Broadcast Packets Statistic Register"]
+ pub RMON_R_BC_PKT: u32,
+ #[doc = "Rx Multicast Packets Statistic Register"]
+ pub RMON_R_MC_PKT: u32,
+ #[doc = "Rx Packets with CRC/Align Error Statistic Register"]
+ pub RMON_R_CRC_ALIGN: u32,
+ #[doc = "Rx Packets with Less Than 64 Bytes and Good CRC Statistic Register"]
+ pub RMON_R_UNDERSIZE: u32,
+ #[doc = "Rx Packets Greater Than MAX_FL and Good CRC Statistic Register"]
+ pub RMON_R_OVERSIZE: u32,
+ #[doc = "Rx Packets Less Than 64 Bytes and Bad CRC Statistic Register"]
+ pub RMON_R_FRAG: u32,
+ #[doc = "Rx Packets Greater Than MAX_FL Bytes and Bad CRC Statistic Register"]
+ pub RMON_R_JAB: u32,
+ _reserved18: [u8; 0x04],
+ #[doc = "Rx 64-Byte Packets Statistic Register"]
+ pub RMON_R_P64: u32,
+ #[doc = "Rx 65- to 127-Byte Packets Statistic Register"]
+ pub RMON_R_P65TO127: u32,
+ #[doc = "Rx 128- to 255-Byte Packets Statistic Register"]
+ pub RMON_R_P128TO255: u32,
+ #[doc = "Rx 256- to 511-Byte Packets Statistic Register"]
+ pub RMON_R_P256TO511: u32,
+ #[doc = "Rx 512- to 1023-Byte Packets Statistic Register"]
+ pub RMON_R_P512TO1023: u32,
+ #[doc = "Rx 1024- to 2047-Byte Packets Statistic Register"]
+ pub RMON_R_P1024TO2047: u32,
+ #[doc = "Rx Packets Greater than 2048 Bytes Statistic Register"]
+ pub RMON_R_P_GTE2048: u32,
+ #[doc = "Rx Octets Statistic Register"]
+ pub RMON_R_OCTETS: u32,
+ #[doc = "Frames not Counted Correctly Statistic Register"]
+ pub IEEE_R_DROP: u32,
+ #[doc = "Frames Received OK Statistic Register"]
+ pub IEEE_R_FRAME_OK: u32,
+ #[doc = "Frames Received with CRC Error Statistic Register"]
+ pub IEEE_R_CRC: u32,
+ #[doc = "Frames Received with Alignment Error Statistic Register"]
+ pub IEEE_R_ALIGN: u32,
+ #[doc = "Receive FIFO Overflow Count Statistic Register"]
+ pub IEEE_R_MACERR: u32,
+ #[doc = "Flow Control Pause Frames Received Statistic Register"]
+ pub IEEE_R_FDXFC: u32,
+ #[doc = "Octet Count for Frames Received without Error Statistic Register"]
+ pub IEEE_R_OCTETS_OK: u32,
+ _reserved19: [u8; 0x011c],
+ #[doc = "Adjustable Timer Control Register"]
+ pub ATCR: u32,
+ #[doc = "Timer Value Register"]
+ pub ATVR: u32,
+ #[doc = "Timer Offset Register"]
+ pub ATOFF: u32,
+ #[doc = "Timer Period Register"]
+ pub ATPER: u32,
+ #[doc = "Timer Correction Register"]
+ pub ATCOR: u32,
+ #[doc = "Time-Stamping Clock Period Register"]
+ pub ATINC: u32,
+ #[doc = "Timestamp of Last Transmitted Frame"]
+ pub ATSTMP: u32,
+ _reserved20: [u8; 0x01e8],
+ #[doc = "Timer Global Status Register"]
+ pub TGSR: u32,
+ #[doc = "Timer Control Status Register"]
+ pub TCSR0: u32,
+ #[doc = "Timer Compare Capture Register"]
+ pub TCCR0: u32,
+ #[doc = "Timer Control Status Register"]
+ pub TCSR1: u32,
+ #[doc = "Timer Compare Capture Register"]
+ pub TCCR1: u32,
+ #[doc = "Timer Control Status Register"]
+ pub TCSR2: u32,
+ #[doc = "Timer Compare Capture Register"]
+ pub TCCR2: u32,
+ #[doc = "Timer Control Status Register"]
+ pub TCSR3: u32,
+ #[doc = "Timer Compare Capture Register"]
+ pub TCCR3: u32,
+}
+
+/// Transfer buffer descriptors.
+pub mod tx_bd {
+ use core::sync::atomic::{AtomicU16, AtomicU32};
+
+ /// Transfer descriptors.
+ #[repr(C)]
+ pub struct TxBD {
+ pub data_length: AtomicU16,
+ pub flags: AtomicU16,
+ pub data_buffer_pointer: AtomicU32,
+ pub errors: AtomicU16,
+ pub control: AtomicU16,
+ pub launch_time: AtomicU32,
+ _reserved0: [u8; 2],
+ pub last_bdu: AtomicU16,
+ pub timestamp_1588: AtomicU32,
+ _reserved1: [u8; 8],
+ }
+
+ pub const FLAGS_READY: u16 = 1 << 15;
+ pub const FLAGS_WRAP: u16 = 1 << 13;
+ pub const FLAGS_LAST_IN_FRAME: u16 = 1 << 11;
+ pub const FLAGS_TRANSMIT_CRC: u16 = 1 << 10;
+
+ pub const CONTROL_INT: u16 = 1 << 14;
+
+ pub const ZEROED: TxBD = TxBD {
+ data_length: AtomicU16::new(0),
+ flags: AtomicU16::new(0),
+ data_buffer_pointer: AtomicU32::new(0),
+ errors: AtomicU16::new(0),
+ control: AtomicU16::new(0),
+ launch_time: AtomicU32::new(0),
+ _reserved0: [0; 2],
+ last_bdu: AtomicU16::new(0),
+ timestamp_1588: AtomicU32::new(0),
+ _reserved1: [0; 8],
+ };
+}
+
+/// Receive buffer descriptors.
+pub mod rx_bd {
+ use core::sync::atomic::{AtomicU16, AtomicU32};
+
+ /// Receive descriptors.
+ #[repr(C)]
+ pub struct RxBD {
+ pub data_length: AtomicU16,
+ pub flags: AtomicU16,
+ pub data_buffer_pointer: AtomicU32,
+ pub status: AtomicU16,
+ pub control: AtomicU16,
+ pub checksum: AtomicU16,
+ pub header: AtomicU16,
+ _reserved0: [u8; 2],
+ pub last_bdu: AtomicU16,
+ pub timestamp_1588: AtomicU32,
+ _reserved1: [u8; 8],
+ }
+
+ pub const FLAGS_EMPTY: u16 = 1 << 15;
+ pub const FLAGS_RECEIVE_OWNERSHP_1: u16 = 1 << 14;
+ pub const FLAGS_WRAP: u16 = 1 << 13;
+ pub const FLAGS_RECEIVE_OWNERSHP_2: u16 = 1 << 12;
+ pub const FLAGS_LAST: u16 = 1 << 11;
+
+ pub const CONTROL_INT: u16 = 1 << 7;
+
+ pub const ZEROED: RxBD = RxBD {
+ data_length: AtomicU16::new(0),
+ flags: AtomicU16::new(0),
+ data_buffer_pointer: AtomicU32::new(0),
+ status: AtomicU16::new(0),
+ control: AtomicU16::new(0),
+ checksum: AtomicU16::new(0),
+ header: AtomicU16::new(0),
+ _reserved0: [0; 2],
+ last_bdu: AtomicU16::new(0),
+ timestamp_1588: AtomicU32::new(0),
+ _reserved1: [0; 8],
+ };
+}
+
+/// Buffer descriptor ring.
+#[repr(align(64))] // Alignment for optimal performance
+pub struct BDRing<D, const N: usize>([D; N]);
+
+impl<D, const N: usize> BDRing<D, N> {
+ pub const fn as_slice(&self) -> &[D] {
+ &self.0
+ }
+}
+
+pub type TxRing<const N: usize> = BDRing<tx_bd::TxBD, N>;
+pub type RxRing<const N: usize> = BDRing<rx_bd::RxBD, N>;
+
+impl<const N: usize> TxRing<N> {
+ pub const ZEROED: Self = Self([const { tx_bd::ZEROED }; N]);
+}
+
+impl<const N: usize> RxRing<N> {
+ pub const ZEROED: Self = Self([const { rx_bd::ZEROED }; N]);
+}
+
+ral_registers::register! {
+ #[doc = "Ethernet Control Register"]
+ pub ECR<u32> RW [
+ #[doc = "Ethernet MAC Reset"]
+ RESET start(0) width(1) RW {}
+ #[doc = "Ethernet Enable"]
+ ETHEREN start(1) width(1) RW {}
+ #[doc = "Magic Packet Detection Enable"]
+ MAGICEN start(2) width(1) RW {}
+ #[doc = "Sleep Mode Enable"]
+ SLEEP start(3) width(1) RW {}
+ #[doc = "EN1588 Enable"]
+ EN1588 start(4) width(1) RW {}
+ #[doc = "Selects between 10/100-Mbit/s and 1000-Mbit/s modes of operation."]
+ SPEED start(5) width(1) RW {}
+ #[doc = "Debug Enable"]
+ DBGEN start(6) width(1) RW {}
+ #[doc = "Descriptor Byte Swapping Enable"]
+ DBSWP start(8) width(1) RW {}
+ #[doc = "S-VLAN enable"]
+ SVLANEN start(9) width(1) RW {}
+ #[doc = "VLAN use second tag"]
+ VLANUSE2ND start(10) width(1) RW {}
+ #[doc = "S-VLAN double tag"]
+ SVLANDBL start(11) width(1) RW {}
+ #[doc = "Transmit clock delay"]
+ TXC_DLY start(16) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Maximum Receive Buffer Size Register - Ring 0"]
+ pub MRBR<u32> RW [
+ #[doc = "Receive buffer size in bytes"]
+ R_BUF_SIZE start(4) width(7) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Transmit Buffer Descriptor Ring 0 Start Register"]
+ pub TDSR<u32> RW []
+}
+
+pub use TDSR as TDSR1;
+pub use TDSR as TDSR2;
+
+ral_registers::register! {
+ #[doc = "Receive Descriptor Ring 0 Start Register"]
+ pub RDSR<u32> RW []
+}
+
+pub use RDSR as RDSR1;
+pub use RDSR as RDSR2;
+
+ral_registers::register! {
+ #[doc = "MII Speed Control Register"]
+ pub MSCR<u32> RW [
+ #[doc = "MII Speed"]
+ MII_SPEED start(1) width(6) RW {}
+ #[doc = "Disable Preamble"]
+ DIS_PRE start(7) width(1) RW {}
+ #[doc = "Hold time On MDIO Output"]
+ HOLDTIME start(8) width(3) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Receive Control Register"]
+ pub RCR<u32> RW [
+ #[doc = "Internal Loopback"]
+ LOOP start(0) width(1) RW {}
+ #[doc = "Disable Receive On Transmit"]
+ DRT start(1) width(1) RW {}
+ #[doc = "Media Independent Interface Mode"]
+ MII_MODE start(2) width(1) RW {}
+ #[doc = "Promiscuous Mode"]
+ PROM start(3) width(1) RW {}
+ #[doc = "Broadcast Frame Reject"]
+ BC_REJ start(4) width(1) RW {}
+ #[doc = "Flow Control Enable"]
+ FCE start(5) width(1) RW {}
+ #[doc = "RGMII Mode Enable"]
+ RGMII_EN start(6) width(1) RW {}
+ #[doc = "RMII Mode Enable"]
+ RMII_MODE start(8) width(1) RW {}
+ #[doc = "Enables 10-Mbit/s mode of the RMII or RGMII ."]
+ RMII_10T start(9) width(1) RW {}
+ #[doc = "Enable Frame Padding Remove On Receive"]
+ PADEN start(12) width(1) RW {}
+ #[doc = "Terminate/Forward Pause Frames"]
+ PAUFWD start(13) width(1) RW {}
+ #[doc = "Terminate/Forward Received CRC"]
+ CRCFWD start(14) width(1) RW {}
+ #[doc = "MAC Control Frame Enable"]
+ CFEN start(15) width(1) RW {}
+ #[doc = "Maximum Frame Length"]
+ MAX_FL start(16) width(14) RW {}
+ #[doc = "Payload Length Check Disable"]
+ NLC start(30) width(1) RW {}
+ #[doc = "Graceful Receive Stopped"]
+ GRS start(31) width(1) RO {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Transmit Control Register"]
+ pub TCR<u32> RW [
+ #[doc = "Graceful Transmit Stop"]
+ GTS start(0) width(1) RW {}
+ #[doc = "Full-Duplex Enable"]
+ FDEN start(2) width(1) RW {}
+ #[doc = "Transmit Frame Control Pause"]
+ TFC_PAUSE start(3) width(1) RW {}
+ #[doc = "Receive Frame Control Pause"]
+ RFC_PAUSE start(4) width(1) RO {}
+ #[doc = "Source MAC Address Select On Transmit"]
+ ADDSEL start(5) width(3) RW {}
+ #[doc = "Set MAC Address On Transmit"]
+ ADDINS start(8) width(1) RW {}
+ #[doc = "Forward Frame From Application With CRC"]
+ CRCFWD start(9) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Transmit FIFO Watermark Register"]
+ pub TFWR<u32> RW [
+ #[doc = "Transmit FIFO Write"]
+ TFWR start(0) width(6) RW {}
+ #[doc = "Store And Forward Enable"]
+ STRFWD start(8) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Receive FIFO Section Full Threshold"]
+ pub RSFL<u32> RW []
+}
+
+ral_registers::register! {
+ #[doc = "Receive Accelerator Function Configuration"]
+ pub RACC<u32> RW [
+ #[doc = "Enable Padding Removal For Short IP Frames"]
+ PADREM start(0) width(1) RW {}
+ #[doc = "Enable Discard Of Frames With Wrong IPv4 Header Checksum"]
+ IPDIS start(1) width(1) RW {}
+ #[doc = "Enable Discard Of Frames With Wrong Protocol Checksum"]
+ PRODIS start(2) width(1) RW {}
+ #[doc = "Enable Discard Of Frames With MAC Layer Errors"]
+ LINEDIS start(6) width(1) RW {}
+ #[doc = "RX FIFO Shift-16"]
+ SHIFT16 start(7) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Transmit Accelerator Function Configuration"]
+ pub TACC<u32> RW [
+ #[doc = "TX FIFO Shift-16"]
+ SHIFT16 start(0) width(1) RW {}
+ #[doc = "Enables insertion of IP header checksum."]
+ IPCHK start(3) width(1) RW {}
+ #[doc = "Enables insertion of protocol checksum."]
+ PROCHK start(4) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Physical Address Lower Register"]
+ pub PALR<u32> RW []
+}
+
+ral_registers::register! {
+ #[doc = "Physical Address Upper Register"]
+ pub PAUR<u32> RW []
+}
+
+ral_registers::register! {
+ #[doc = "MIB Control Register"]
+ pub MIBC<u32> RW [
+ #[doc = "MIB Clear"]
+ MIB_CLEAR start(29) width(1) RW {}
+ #[doc = "MIB Idle"]
+ MIB_IDLE start(30) width(1) RO {}
+ #[doc = "Disable MIB Logic"]
+ MIB_DIS start(31) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Transmit Descriptor Active Register - Ring 0"]
+ pub TDAR<u32> RW [
+ #[doc = "Transmit Descriptor Active"]
+ TDAR start(24) width(1) RW {}
+ ]
+}
+
+pub use TDAR as TDAR1;
+pub use TDAR as TDAR2;
+
+ral_registers::register! {
+ #[doc = "Receive Descriptor Active Register - Ring 0"]
+ pub RDAR<u32> RW [
+ #[doc = "Receive Descriptor Active"]
+ RDAR start(24) width(1) RW {}
+ ]
+}
+
+pub use RDAR as RDAR1;
+pub use RDAR as RDAR2;
+
+ral_registers::register! {
+ #[doc = "Interrupt Event Register"]
+ pub EIR<u32> RW [
+ #[doc = "Receive buffer interrupt, class 1"]
+ RXB1 start(0) width(1) RW {}
+ #[doc = "Receive frame interrupt, class 1"]
+ RXF1 start(1) width(1) RW {}
+ #[doc = "Transmit buffer interrupt, class 1"]
+ TXB1 start(2) width(1) RW {}
+ #[doc = "Transmit frame interrupt, class 1"]
+ TXF1 start(3) width(1) RW {}
+ #[doc = "Receive buffer interrupt, class 2"]
+ RXB2 start(4) width(1) RW {}
+ #[doc = "Receive frame interrupt, class 2"]
+ RXF2 start(5) width(1) RW {}
+ #[doc = "Transmit buffer interrupt, class 2"]
+ TXB2 start(6) width(1) RW {}
+ #[doc = "Transmit frame interrupt, class 2"]
+ TXF2 start(7) width(1) RW {}
+ #[doc = "RX DMA Ring 0 flush indication"]
+ RXFLUSH_0 start(12) width(1) RW {}
+ #[doc = "RX DMA Ring 1 flush indication"]
+ RXFLUSH_1 start(13) width(1) RW {}
+ #[doc = "RX DMA Ring 2 flush indication"]
+ RXFLUSH_2 start(14) width(1) RW {}
+ #[doc = "Timestamp Timer"]
+ TS_TIMER start(15) width(1) RW {}
+ #[doc = "Transmit Timestamp Available"]
+ TS_AVAIL start(16) width(1) RW {}
+ #[doc = "Node Wakeup Request Indication"]
+ WAKEUP start(17) width(1) RW {}
+ #[doc = "Payload Receive Error"]
+ PLR start(18) width(1) RW {}
+ #[doc = "Transmit FIFO Underrun"]
+ UN start(19) width(1) RW {}
+ #[doc = "Collision Retry Limit"]
+ RL start(20) width(1) RW {}
+ #[doc = "Late Collision"]
+ LC start(21) width(1) RW {}
+ #[doc = "Ethernet Bus Error"]
+ EBERR start(22) width(1) RW {}
+ #[doc = "MII Interrupt."]
+ MII start(23) width(1) RW {}
+ #[doc = "Receive Buffer Interrupt"]
+ RXB start(24) width(1) RW {}
+ #[doc = "Receive Frame Interrupt"]
+ RXF start(25) width(1) RW {}
+ #[doc = "Transmit Buffer Interrupt"]
+ TXB start(26) width(1) RW {}
+ #[doc = "Transmit Frame Interrupt"]
+ TXF start(27) width(1) RW {}
+ #[doc = "Graceful Stop Complete"]
+ GRA start(28) width(1) RW {}
+ #[doc = "Babbling Transmit Error"]
+ BABT start(29) width(1) RW {}
+ #[doc = "Babbling Receive Error"]
+ BABR start(30) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Interrupt Mask Register"]
+ pub EIMR<u32> RW [
+ #[doc = "Receive buffer interrupt, class 1"]
+ RXB1 start(0) width(1) RW {}
+ #[doc = "Receive frame interrupt, class 1"]
+ RXF1 start(1) width(1) RW {}
+ #[doc = "Transmit buffer interrupt, class 1"]
+ TXB1 start(2) width(1) RW {}
+ #[doc = "Transmit frame interrupt, class 1"]
+ TXF1 start(3) width(1) RW {}
+ #[doc = "Receive buffer interrupt, class 2"]
+ RXB2 start(4) width(1) RW {}
+ #[doc = "Receive frame interrupt, class 2"]
+ RXF2 start(5) width(1) RW {}
+ #[doc = "Transmit buffer interrupt, class 2"]
+ TXB2 start(6) width(1) RW {}
+ #[doc = "Transmit frame interrupt, class 2"]
+ TXF2 start(7) width(1) RW {}
+ #[doc = "Corresponds to interrupt source EIR[RXFLUSH_0] and determines whether an interrupt condition can generate an interrupt"]
+ RXFLUSH_0 start(12) width(1) RW {}
+ #[doc = "Corresponds to interrupt source EIR[RXFLUSH_1] and determines whether an interrupt condition can generate an interrupt"]
+ RXFLUSH_1 start(13) width(1) RW {}
+ #[doc = "Corresponds to interrupt source EIR[RXFLUSH_2] and determines whether an interrupt condition can generate an interrupt"]
+ RXFLUSH_2 start(14) width(1) RW {}
+ #[doc = "TS_TIMER Interrupt Mask"]
+ TS_TIMER start(15) width(1) RW {}
+ #[doc = "TS_AVAIL Interrupt Mask"]
+ TS_AVAIL start(16) width(1) RW {}
+ #[doc = "WAKEUP Interrupt Mask"]
+ WAKEUP start(17) width(1) RW {}
+ #[doc = "PLR Interrupt Mask"]
+ PLR start(18) width(1) RW {}
+ #[doc = "UN Interrupt Mask"]
+ UN start(19) width(1) RW {}
+ #[doc = "RL Interrupt Mask"]
+ RL start(20) width(1) RW {}
+ #[doc = "LC Interrupt Mask"]
+ LC start(21) width(1) RW {}
+ #[doc = "EBERR Interrupt Mask"]
+ EBERR start(22) width(1) RW {}
+ #[doc = "MII Interrupt Mask"]
+ MII start(23) width(1) RW {}
+ #[doc = "RXB Interrupt Mask"]
+ RXB start(24) width(1) RW {}
+ #[doc = "RXF Interrupt Mask"]
+ RXF start(25) width(1) RW {}
+ #[doc = "TXB Interrupt Mask"]
+ TXB start(26) width(1) RW {}
+ #[doc = "TXF Interrupt Mask"]
+ TXF start(27) width(1) RW {}
+ #[doc = "GRA Interrupt Mask"]
+ GRA start(28) width(1) RW {}
+ #[doc = "BABT Interrupt Mask"]
+ BABT start(29) width(1) RW {}
+ #[doc = "BABR Interrupt Mask"]
+ BABR start(30) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "MII Management Frame Register"]
+ pub MMFR<u32> RW [
+ #[doc = "Management Frame Data"]
+ DATA start(0) width(16) RW {}
+ #[doc = "Turn Around"]
+ TA start(16) width(2) RW {}
+ #[doc = "Register Address"]
+ RA start(18) width(5) RW {}
+ #[doc = "PHY Address"]
+ PA start(23) width(5) RW {}
+ #[doc = "Operation Code"]
+ OP start(28) width(2) RW {}
+ #[doc = "Start Of Frame Delimiter"]
+ ST start(30) width(2) RW {}
+ ]
+}
+
+#[cfg(test)]
+mod tests {
+ use super::RegisterBlock;
+ use core::mem::offset_of;
+
+ #[test]
+ fn layout() {
+ assert_eq!(offset_of!(RegisterBlock, EIR), 0x04);
+ assert_eq!(offset_of!(RegisterBlock, MMFR), 0x40);
+ assert_eq!(offset_of!(RegisterBlock, MSCR), 0x44);
+ assert_eq!(offset_of!(RegisterBlock, TACC), 0x1C0);
+ assert_eq!(offset_of!(RegisterBlock, RACC), 0x1C4);
+ }
+}
diff --git a/drivers/flexspi/Cargo.toml b/drivers/flexspi/Cargo.toml
new file mode 100644
index 0000000..1b33074
--- /dev/null
+++ b/drivers/flexspi/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "imxrt-drivers-flexspi"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+ral-registers = { workspace = true }
diff --git a/drivers/flexspi/src/lib.rs b/drivers/flexspi/src/lib.rs
new file mode 100644
index 0000000..b7d3a11
--- /dev/null
+++ b/drivers/flexspi/src/lib.rs
@@ -0,0 +1,882 @@
+#![no_std]
+
+pub type Instance = ral_registers::Instance<RegisterBlock>;
+
+#[repr(C)]
+#[allow(non_snake_case)]
+pub struct RegisterBlock {
+ #[doc = "Module Control Register 0"]
+ pub MCR0: u32,
+ #[doc = "Module Control Register 1"]
+ pub MCR1: u32,
+ #[doc = "Module Control Register 2"]
+ pub MCR2: u32,
+ #[doc = "AHB Bus Control Register"]
+ pub AHBCR: u32,
+ #[doc = "Interrupt Enable Register"]
+ pub INTEN: u32,
+ #[doc = "Interrupt Register"]
+ pub INTR: u32,
+ #[doc = "LUT Key Register"]
+ pub LUTKEY: u32,
+ #[doc = "LUT Control Register"]
+ pub LUTCR: u32,
+
+ #[doc = "AHB RX Buffer Control Register 0"]
+ pub AHBRXBUFCR0: [u32; 8],
+ _reserved0: [u8; 0x20],
+
+ #[doc = "Flash Control Register 0"]
+ pub FLSHCR0: [u32; 4],
+ #[doc = "Flash Control Register 1"]
+ pub FLSHCR1: [u32; 4],
+ #[doc = "Flash Control Register 2"]
+ pub FLSHCR2: [u32; 4],
+ _reserved1: [u8; 4],
+ pub FLSHCR4: u32,
+ _reserved2: [u8; 0x08],
+
+ #[doc = "IP Control Register 0"]
+ pub IPCR0: u32,
+ #[doc = "IP Control Register 1"]
+ pub IPCR1: u32,
+ _reserved3: [u8; 0x08],
+ #[doc = "IP Command Register"]
+ pub IPCMD: u32,
+ _reserved4: [u8; 0x04],
+ #[doc = "IP RX FIFO Control Register"]
+ pub IPRXFCR: u32,
+ #[doc = "IP TX FIFO Control Register"]
+ pub IPTXFCR: u32,
+ #[doc = "DLL Control Register 0"]
+ pub DLLCR: [u32; 2usize],
+ _reserved5: [u8; 0x18],
+ #[doc = "Status Register 0"]
+ pub STS0: u32,
+ #[doc = "Status Register 1"]
+ pub STS1: u32,
+ #[doc = "Status Register 2"]
+ pub STS2: u32,
+ #[doc = "AHB Suspend Status Register"]
+ pub AHBSPNDSTS: u32,
+ #[doc = "IP RX FIFO Status Register"]
+ pub IPRXFSTS: u32,
+ #[doc = "IP TX FIFO Status Register"]
+ pub IPTXFSTS: u32,
+ _reserved6: [u8; 0x08],
+ #[doc = "IP RX FIFO Data Register 0"]
+ pub RFDR: [u32; 32usize],
+ #[doc = "IP TX FIFO Data Register 0"]
+ pub TFDR: [u32; 32usize],
+ #[doc = "LUT 0"]
+ pub LUT: [u32; 64usize],
+}
+
+/// Port A, device 1.
+pub const A1: usize = 0;
+/// Port A, device 2.
+pub const A2: usize = 1;
+/// Port B, device 1.
+pub const B1: usize = 2;
+/// Port B, device 2.
+pub const B2: usize = 3;
+
+/// Port A DLL access.
+pub const DLL_A: usize = 0;
+/// Port B DLL access.
+pub const DLL_B: usize = 1;
+
+/// A FlexSPI instruction opcode.
+#[derive(Clone, Copy, PartialEq, Eq)]
+#[repr(transparent)]
+pub struct Opcode(pub u8);
+
+/// Stop execution, deassert CS. Next command sequence
+/// (to the same flash device) will started from instruction pointer 0.
+pub const STOP: Opcode = Opcode(0x00);
+
+/// Stop execution, deassert CS and save the operand
+/// as the instruction start pointer for next sequence.
+///
+/// Normally this instruction is used to support XIP enhance mode.
+pub const JUMP_ON_CS: Opcode = Opcode(0x1F);
+
+/// Transmit command code to flash
+pub const SDR_CMD: Opcode = Opcode(0x01);
+/// Transmit row address to flash
+pub const SDR_RADDR: Opcode = Opcode(0x02);
+/// Transmit column address to flash
+pub const SDR_CADDR: Opcode = Opcode(0x03);
+/// Transmit mode bits to flash
+///
+/// Bit number 1
+pub const SDR_MODE1: Opcode = Opcode(0x04);
+/// Transmit mode bits to flash
+///
+/// Bit number 2
+pub const SDR_MODE2: Opcode = Opcode(0x05);
+/// Transmit mode bits to flash
+///
+/// Bit number 4
+pub const SDR_MODE4: Opcode = Opcode(0x06);
+/// Transmit mode bits to flash
+///
+/// Bit number 8
+pub const SDR_MODE8: Opcode = Opcode(0x07);
+/// Transmit programming data to flash
+pub const SDR_WRITE: Opcode = Opcode(0x08);
+/// Receive data from flash
+///
+/// Read Data is put into AHB_RX_BUF or IP_RX_FIFO.
+pub const SDR_READ: Opcode = Opcode(0x09);
+/// Receive Read Data or Preamble bit from Flash device
+///
+/// FlexSPI Controller will compare the data line bits with DLPR
+/// register to determine a correct sampling clock phase.
+pub const SDR_LEARN: Opcode = Opcode(0x0A);
+/// Transmit Read/ Program Data size (byte number) to Flash
+pub const SDR_DATASZ: Opcode = Opcode(0x0B);
+/// Leave data lines undriven by FlexSPI controller.
+///
+/// Provide turnaround cycles from host driving to device driving.
+/// `num_pads` will determine the number of pads in input mode.
+pub const SDR_DUMMY: Opcode = Opcode(0x0C);
+/// Similar to `DUMMY`, but the cycle number is different
+pub const SDR_DUMMY_RWDS: Opcode = Opcode(0x0D);
+
+/// Turn an SDR opcode into a DDR opcode.
+const fn sdr_to_ddr(opcode: Opcode) -> Opcode {
+ Opcode(opcode.0 + 0x20)
+}
+
+/// Transmit command code to flash
+pub const DDR_CMD: Opcode = sdr_to_ddr(SDR_CMD);
+/// Transmit row address to flash
+pub const DDR_RADDR: Opcode = sdr_to_ddr(SDR_RADDR);
+/// Transmit column address to flash
+pub const DDR_CADDR: Opcode = sdr_to_ddr(SDR_CADDR);
+/// Transmit mode bits to flash
+///
+/// Bit number 1
+pub const DDR_MODE1: Opcode = sdr_to_ddr(SDR_MODE1);
+/// Transmit mode bits to flash
+///
+/// Bit number 2
+pub const DDR_MODE2: Opcode = sdr_to_ddr(SDR_MODE2);
+/// Transmit mode bits to flash
+///
+/// Bit number 4
+pub const DDR_MODE4: Opcode = sdr_to_ddr(SDR_MODE4);
+/// Transmit mode bits to flash
+///
+/// Bit number 8
+pub const DDR_MODE8: Opcode = sdr_to_ddr(SDR_MODE8);
+/// Transmit programming data to flash
+pub const DDR_WRITE: Opcode = sdr_to_ddr(SDR_WRITE);
+/// Receive data from flash
+///
+/// Read Data is put into AHB_RX_BUF or IP_RX_FIFO.
+pub const DDR_READ: Opcode = sdr_to_ddr(SDR_READ);
+/// Receive Read Data or Preamble bit from Flash device
+///
+/// FlexSPI Controller will compare the data line bits with DLPR
+/// register to determine a correct sampling clock phase.
+pub const DDR_LEARN: Opcode = sdr_to_ddr(SDR_LEARN);
+/// Transmit Read/ Program Data size (byte number) to Flash
+pub const DDR_DATASZ: Opcode = sdr_to_ddr(SDR_DATASZ);
+/// Leave data lines undriven by FlexSPI controller.
+///
+/// Provide turnaround cycles from host driving to device driving.
+/// `num_pads` will determine the number of pads in input mode.
+pub const DDR_DUMMY: Opcode = sdr_to_ddr(SDR_DUMMY);
+/// Similar to `DUMMY`, but the cycle number is different
+pub const DDR_DUMMY_RWDS: Opcode = sdr_to_ddr(SDR_DUMMY_RWDS);
+
+/// Number of pads to use to execute the instruction
+#[derive(Clone, Copy)]
+#[repr(u8)]
+pub enum Pads {
+ /// Single mode
+ One = 0x00,
+ /// Dual mode
+ Two = 0x01,
+ /// Quad mode
+ Four = 0x02,
+ /// Octal mode
+ Eight = 0x03,
+}
+
+/// FlexSPI LUT instruction.
+#[derive(Clone, Copy, PartialEq, Eq)]
+#[repr(transparent)]
+pub struct Instr(u16);
+
+impl Instr {
+ /// Form a new instruction.
+ #[must_use]
+ pub const fn new(opcode: Opcode, pads: Pads, operand: u8) -> Self {
+ let opcode = (opcode.0 as u16) << 10;
+ let pads = (pads as u16) << 8;
+ let operand = operand as u16;
+ Self(opcode | pads | operand)
+ }
+
+ /// Convenience for a `STOP` instruction.
+ pub const STOP: Self = Instr::new(STOP, Pads::One, 0);
+ /// Convenience for a `JUMP_ON_CS` instruction.
+ pub const JUMP_ON_CS: Self = Self::new(JUMP_ON_CS, Pads::One, 0);
+}
+
+/// A FlexSPI LUT instruction sequence.
+#[repr(align(4))]
+pub struct Sequence(pub [Instr; 8]);
+
+const _: () = assert!(size_of::<Sequence>() == size_of::<[u32; 4]>());
+
+/// The maximum number of sequences that can
+/// be executed per IP command.
+///
+/// Besides respecting this constraing, you also
+/// must remain in bounds of the lookup table.
+pub const MAX_SEQ_PER_IP_CMD: usize = 8;
+
+/// The number of sequences in the lookup table.
+pub const LUT_SIZE: usize = 16;
+
+/// A sequence identifier.
+///
+/// Describes the location of the sequence in the
+/// LUT.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+#[repr(usize)]
+pub enum SeqId {
+ Seq00,
+ Seq01,
+ Seq02,
+ Seq03,
+ Seq04,
+ Seq05,
+ Seq06,
+ Seq07,
+ Seq08,
+ Seq09,
+ Seq10,
+ Seq11,
+ Seq12,
+ Seq13,
+ Seq14,
+ Seq15,
+}
+
+impl SeqId {
+ /// Produce the next sequence ID.
+ ///
+ /// Returns `None` if the next ID is invalid.
+ pub const fn next_id(self) -> Option<Self> {
+ // Safety: raw value remains in bounds of enum
+ // representation.
+ unsafe {
+ let raw = (self as usize) + 1;
+ if raw < 16 {
+ Some(core::mem::transmute(raw))
+ } else {
+ None
+ }
+ }
+ }
+}
+
+/// An iterator over sequence IDs.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct SeqIdRange(Option<SeqId>);
+
+impl SeqIdRange {
+ /// Returns a sequence ID range with no values.
+ pub const fn empty() -> Self {
+ Self(None)
+ }
+ /// Returns a range starting from the given ID.
+ pub const fn start(seq_id: SeqId) -> Self {
+ Self(Some(seq_id))
+ }
+}
+
+impl Iterator for SeqIdRange {
+ type Item = SeqId;
+ fn next(&mut self) -> Option<Self::Item> {
+ let this = self.0?;
+ let next = this.next_id();
+ self.0 = next;
+ Some(this)
+ }
+}
+
+/// Install an instruction sequence into the FlexSPI LUT.
+///
+/// The implementation assumes that you've already unlocked the LUT.
+pub fn set_sequence(flexspi: Instance, sequence_id: SeqId, sequence: &Sequence) {
+ // Safety: see inline notes.
+ unsafe {
+ // The LUT is a contiguous collection of sequences, and each
+ // sequence is a contiguous collection of instructions.
+ let lut: *mut [u32; 64] = (&raw mut (*flexspi.as_ptr()).LUT);
+ let lut: *mut Sequence = lut.cast();
+
+ // We remain in bounds of the LUT, even if the MMIO
+ // allocation supports more sequences.
+ let lut: *mut Sequence = lut.add(sequence_id as usize);
+
+ // 8 instructions per sequence, and each instruction
+ // is a u16. We can treat that as four u32s on this
+ // target. Sequence is four-byte aligned for u32 access.
+ let lut: *mut u32 = lut.cast();
+ let seq: *const u32 = (sequence as *const Sequence).cast();
+
+ // Accesses remain in bounds of the caller's sequence
+ // and LUT's sequence.
+ lut.add(0).write_volatile(seq.add(0).read());
+ lut.add(1).write_volatile(seq.add(1).read());
+ lut.add(2).write_volatile(seq.add(2).read());
+ lut.add(3).write_volatile(seq.add(3).read());
+ }
+}
+
+/// Unlock the LUT before installing sequences.
+#[inline]
+pub fn unlock_lut(flexspi: Instance) {
+ ral_registers::write_reg!(self, flexspi, LUTKEY, LUTKEY_MAGIC);
+ ral_registers::write_reg!(self, flexspi, LUTCR, UNLOCK: 1, LOCK: 0);
+}
+
+/// Lock the LUT after installing sequences.
+#[inline]
+pub fn lock_lut(flexspi: Instance) {
+ ral_registers::write_reg!(self, flexspi, LUTKEY, LUTKEY_MAGIC);
+ ral_registers::write_reg!(self, flexspi, LUTCR, UNLOCK: 0, LOCK: 1);
+}
+
+ral_registers::register! {
+ #[doc = "Module Control Register 0"]
+ pub MCR0<u32> RW [
+ #[doc = "Software Reset"]
+ SWRESET start(0) width(1) RW {}
+ #[doc = "Module Disable"]
+ MDIS start(1) width(1) RW {}
+ #[doc = "Sample Clock source selection for Flash Reading"]
+ RXCLKSRC start(4) width(2) RW {
+ #[doc = "Dummy Read strobe generated by FlexSPI Controller and loopback internally."]
+ LOOPBACK_INTERNAL = 0,
+ #[doc = "Dummy Read strobe generated by FlexSPI Controller and loopback from DQS pad."]
+ LOOPBACK_DQS = 0x1,
+ #[doc = "Flash provided Read strobe and input from DQS pad"]
+ FLASH_DQS = 0x3,
+ }
+ #[doc = "Enable AHB bus Read Access to IP RX FIFO."]
+ ARDFEN start(6) width(1) RW {
+ #[doc = "IP RX FIFO should be read by IP Bus. AHB Bus read access to IP RX FIFO memory space will get bus error response."]
+ IP_BUS = 0,
+ #[doc = "IP RX FIFO should be read by AHB Bus. IP Bus read access to IP RX FIFO memory space will always return data zero but no bus error response."]
+ AHB_BUS = 0x1,
+ }
+ #[doc = "Enable AHB bus Write Access to IP TX FIFO."]
+ ATDFEN start(7) width(1) RW {
+ #[doc = "IP TX FIFO should be written by IP Bus. AHB Bus write access to IP TX FIFO memory space will get bus error response."]
+ IP_BUS = 0,
+ #[doc = "IP TX FIFO should be written by AHB Bus. IP Bus write access to IP TX FIFO memory space will be ignored but no bus error response."]
+ AHB_BUS = 0x1,
+ }
+ #[doc = "The serial root clock could be divided inside FlexSPI . Refer Clocks chapter for more details on clocking."]
+ SERCLKDIV start(8) width(3) RW {
+ #[doc = "Divided by 1"]
+ DIVIDE_1 = 0,
+ #[doc = "Divided by 2"]
+ DIVIDE_2 = 0x1,
+ #[doc = "Divided by 3"]
+ DIVIDE_3 = 0x2,
+ #[doc = "Divided by 4"]
+ DIVIDE_4 = 0x3,
+ #[doc = "Divided by 5"]
+ DIVIDE_5 = 0x4,
+ #[doc = "Divided by 6"]
+ DIVIDE_6 = 0x5,
+ #[doc = "Divided by 7"]
+ DIVIDE_7 = 0x6,
+ #[doc = "Divided by 8"]
+ DIVIDE_8 = 0x7,
+ }
+ #[doc = "Half Speed Serial Flash access Enable."]
+ HSEN start(11) width(1) RW {}
+ #[doc = "Doze mode enable bit"]
+ DOZEEN start(12) width(1) RW {}
+ #[doc = "This bit is to support Flash Octal mode access by combining Port A and B Data pins (A_DATA[3:0] and B_DATA[3:0]), when Port A and Port B are of 4 bit data width."]
+ COMBINATIONEN start(13) width(1) RW {}
+ #[doc = "This bit is used to force SCLK output free-running. For FPGA applications, external device may use SCLK as reference clock to its internal PLL. If SCLK free-running is enabled, data sampling with loopback clock from SCLK pad is not supported (MCR0[RXCLKSRC]=2)."]
+ SCKFREERUNEN start(14) width(1) RW {}
+ #[doc = "Time out wait cycle for IP command grant."]
+ IPGRANTWAIT start(16) width(8) RW {}
+ #[doc = "Timeout wait cycle for AHB command grant."]
+ AHBGRANTWAIT start(24) width(8) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Module Control Register 1"]
+ pub MCR1<u32> RW [
+ #[doc = "AHB Read/Write access to Serial Flash Memory space will timeout if not data received from Flash or data not transmitted after AHBBUSWAIT * 1024 ahb clock cycles, AHB Bus will get an error response"]
+ AHBBUSWAIT start(0) width(16) RW {}
+ #[doc = "Command Sequence Execution will timeout and abort after SEQWAIT * 1024 Serial Root Clock cycles"]
+ SEQWAIT start(16) width(16) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Module Control Register 2"]
+ pub MCR2<u32> RW [
+ #[doc = "This bit determines whether AHB RX Buffer and AHB TX Buffer will be cleaned automatically when FlexSPI returns STOP mode ACK. Software should set this bit if AHB RX Buffer or AHB TX Buffer will be powered off in STOP mode. Otherwise AHB read access after exiting STOP mode may hit AHB RX Buffer or AHB TX Buffer but their data entries are invalid."]
+ CLRAHBBUFOPT start(11) width(1) RW {}
+ #[doc = "All external devices are same devices (both in types and size) for A1/A2/B1/B2."]
+ SAMEDEVICEEN start(15) width(1) RW {}
+ #[doc = "B_SCLK pad can be used as A_SCLK differential clock output (inverted clock to A_SCLK). In this case, port B flash access is not available. After changing the value of this field, MCR0[SWRESET] should be set."]
+ SCKBDIFFOPT start(19) width(1) RW {}
+ #[doc = "Wait cycle (in AHB clock cycle) for idle state before suspended command sequence resumed."]
+ RESUMEWAIT start(24) width(8) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "AHB Bus Control Register"]
+ pub AHBCR<u32> RW [
+ #[doc = "Parallel mode enabled for AHB triggered Command (both read and write) ."]
+ APAREN start(0) width(1) RW {}
+ #[doc = "Clear the status/pointers of AHB RX Buffer. Auto-cleared."]
+ CLRAHBRXBUF start(1) width(1) RW {}
+ #[doc = "Enable AHB bus cachable read access support."]
+ CACHABLEEN start(3) width(1) RW {}
+ #[doc = "Enable AHB bus bufferable write access support. This field affects the last beat of AHB write access, refer for more details about AHB bufferable write."]
+ BUFFERABLEEN start(4) width(1) RW {}
+ #[doc = "AHB Read Prefetch Enable."]
+ PREFETCHEN start(5) width(1) RW {}
+ #[doc = "AHB Read Address option bit. This option bit is intend to remove AHB burst start address alignment limitation."]
+ READADDROPT start(6) width(1) RW {}
+ #[doc = "AHB Read Size Alignment"]
+ READSZALIGN start(10) width(1) RW {}
+ #[doc = "AHB Read ECC Enable"]
+ ECCEN start(11) width(1) RW {}
+ #[doc = "AHB transaction SPLIT"]
+ SPLITEN start(12) width(1) RW {}
+ #[doc = "AHB SPLIT SIZE"]
+ SPLIT_LIMIT start(13) width(2) RW {
+ #[doc = "AHB Split Size=8bytes"]
+ SPLIT_8 = 0,
+ #[doc = "AHB Split Size=16bytes"]
+ SPLIT_16 = 0x1,
+ #[doc = "AHB Split Size=32bytes"]
+ SPLIT_32 = 0x2,
+ #[doc = "AHB Split Size=64bytes"]
+ SPLIT_64 = 0x3,
+ }
+ #[doc = "OTFAD KEY BLOC ECC Enable"]
+ KEYECCEN start(15) width(1) RW {}
+ #[doc = "AHB ECC Single bit ERR CLR"]
+ ECCSINGLEERRCLR start(16) width(1) RW {}
+ #[doc = "AHB ECC Multi bits ERR CLR"]
+ ECCMULTIERRCLR start(17) width(1) RW {}
+ #[doc = "AHB Master ID Remapping enable"]
+ HMSTRIDREMAP start(18) width(1) RW {}
+ #[doc = "ECC Read data swap function"]
+ ECCSWAPEN start(19) width(1) RW {}
+ #[doc = "Decides all AHB read/write boundary. All access cross the boundary will be divided into smaller sub accesses."]
+ ALIGNMENT start(20) width(2) RW {
+ #[doc = "No limit"]
+ NO_LIMIT = 0,
+ #[doc = "1 KBytes"]
+ BYTES_1024 = 0x1,
+ #[doc = "512 Bytes"]
+ BYTES_512 = 0x2,
+ #[doc = "256 Bytes"]
+ BYTES_256 = 0x3,
+ }
+ ]
+}
+
+/// Interrupt Enable Register.
+#[allow(non_snake_case)]
+pub mod INTEN {
+ pub use super::INTR::*;
+}
+
+ral_registers::register! {
+ #[doc = "Interrupt Register"]
+ pub INTR<u32> RW [
+ #[doc = "IP triggered Command Sequences Execution finished interrupt. This interrupt is also generated when there is IPCMDGE or IPCMDERR interrupt generated."]
+ IPCMDDONE start(0) width(1) RW {}
+ #[doc = "IP triggered Command Sequences Grant Timeout interrupt."]
+ IPCMDGE start(1) width(1) RW {}
+ #[doc = "AHB triggered Command Sequences Grant Timeout interrupt."]
+ AHBCMDGE start(2) width(1) RW {}
+ #[doc = "IP triggered Command Sequences Error Detected interrupt. When an error detected for IP command, this command will be ignored and not executed at all."]
+ IPCMDERR start(3) width(1) RW {}
+ #[doc = "AHB triggered Command Sequences Error Detected interrupt. When an error detected for AHB command, this command will be ignored and not executed at all."]
+ AHBCMDERR start(4) width(1) RW {}
+ #[doc = "IP RX FIFO watermark available interrupt."]
+ IPRXWA start(5) width(1) RW {}
+ #[doc = "IP TX FIFO watermark empty interrupt."]
+ IPTXWE start(6) width(1) RW {}
+ #[doc = "SCLK is stopped during command sequence because Async RX FIFO full interrupt."]
+ SCKSTOPBYRD start(8) width(1) RW {}
+ #[doc = "SCLK is stopped during command sequence because Async TX FIFO empty interrupt."]
+ SCKSTOPBYWR start(9) width(1) RW {}
+ #[doc = "AHB Bus timeout or AHB bus illegal access Flash during OTFAD key blob processing interrupt."]
+ AHBBUSERROR start(10) width(1) RW {}
+ #[doc = "Sequence execution timeout interrupt."]
+ SEQTIMEOUT start(11) width(1) RW {}
+ #[doc = "OTFAD key blob processing done interrupt."]
+ KEYDONE start(12) width(1) RW {}
+ #[doc = "OTFAD key blob processing error interrupt."]
+ KEYERROR start(13) width(1) RW {}
+ #[doc = "ECC multi bits error interrupt."]
+ ECCMULTIERR start(14) width(1) RW {}
+ #[doc = "ECC single bit error interrupt."]
+ ECCSINGLEERR start(15) width(1) RW {}
+ #[doc = "IP command security violation interrupt."]
+ IPCMDSECUREVIO start(16) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "LUT Key Register"]
+ pub LUTKEY<u32> RW []
+}
+
+pub const LUTKEY_MAGIC: u32 = 0x5AF05AF0;
+
+ral_registers::register! {
+ #[doc = "LUT Control Register"]
+ pub LUTCR<u32> RW [
+ #[doc = "Lock LUT"]
+ LOCK start(0) width(1) RW {}
+ #[doc = "Unlock LUT"]
+ UNLOCK start(1) width(1) RW {}
+ #[doc = "LUT protection"]
+ PROTECT start(2) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "AHB RX Buffer Control Register 0"]
+ pub AHBRXBUFCR0<u32> RW [
+ #[doc = "AHB RX Buffer Size in 64 bits."]
+ BUFSZ start(0) width(10) RW {}
+ #[doc = "This AHB RX Buffer is assigned according to AHB Master with ID (MSTR_ID)."]
+ MSTRID start(16) width(4) RW {}
+ #[doc = "This priority for AHB Master Read which this AHB RX Buffer is assigned. 7 is the highest priority, 0 the lowest."]
+ PRIORITY start(24) width(3) RW {}
+ #[doc = "AHB RX Buffer address region funciton enable"]
+ REGIONEN start(30) width(1) RW {}
+ #[doc = "AHB Read Prefetch Enable for current AHB RX Buffer corresponding Master."]
+ PREFETCHEN start(31) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Flash Control Register 0"]
+ pub FLSHCR0<u32> RW [
+ #[doc = "Flash Size in KByte."]
+ FLSHSZ start(0) width(23) RW {}
+ #[doc = "AHB write access split function control."]
+ SPLITWREN start(30) width(1) RW {}
+ #[doc = "AHB read access split function control."]
+ SPLITRDEN start(31) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Flash Control Register 1"]
+ pub FLSHCR1<u32> RW [
+ #[doc = "Serial Flash CS setup time."]
+ TCSS start(0) width(5) RW {}
+ #[doc = "Serial Flash CS Hold time."]
+ TCSH start(5) width(5) RW {}
+ #[doc = "Word Addressable."]
+ WA start(10) width(1) RW {}
+ #[doc = "Column Address Size."]
+ CAS start(11) width(4) RW {}
+ #[doc = "CS interval unit"]
+ CSINTERVALUNIT start(15) width(1) RW {}
+ #[doc = "This field is used to set the minimum interval between flash device Chip selection deassertion and flash device Chip selection assertion. If external flash has a limitation on the interval between command sequences, this field should be set accordingly. If there is no limitation, set this field with value 0x0."]
+ CSINTERVAL start(16) width(16) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Flash Control Register 2"]
+ pub FLSHCR2<u32> RW [
+ #[doc = "Sequence Index for AHB Read triggered Command in LUT."]
+ ARDSEQID start(0) width(4) RW {}
+ #[doc = "Sequence Number for AHB Read triggered Command in LUT."]
+ ARDSEQNUM start(5) width(3) RW {}
+ #[doc = "Sequence Index for AHB Write triggered Command."]
+ AWRSEQID start(8) width(4) RW {}
+ #[doc = "Sequence Number for AHB Write triggered Command."]
+ AWRSEQNUM start(13) width(3) RW {}
+ #[doc = "For certain devices (such as FPGA), it need some time to write data into internal memory after the command sequences finished on FlexSPI interface"]
+ AWRWAIT start(16) width(12) RW {}
+ #[doc = "AWRWAIT unit"]
+ AWRWAITUNIT start(28) width(3) RW {
+ #[doc = "The AWRWAIT unit is 2 ahb clock cycle"]
+ AHB_CYCLE_2 = 0,
+ #[doc = "The AWRWAIT unit is 8 ahb clock cycle"]
+ AHB_CYCLE_8 = 1,
+ #[doc = "The AWRWAIT unit is 32 ahb clock cycle"]
+ AHB_CYCLE_32 = 2,
+ #[doc = "The AWRWAIT unit is 128 ahb clock cycle"]
+ AHB_CYCLE_128 = 3,
+ #[doc = "The AWRWAIT unit is 512 ahb clock cycle"]
+ AHB_CYCLE_512 = 4,
+ #[doc = "The AWRWAIT unit is 2048 ahb clock cycle"]
+ AHB_CYCLE_2048 = 5,
+ #[doc = "The AWRWAIT unit is 8192 ahb clock cycle"]
+ AHB_CYCLE_8192 = 6,
+ #[doc = "The AWRWAIT unit is 32768 ahb clock cycle"]
+ AHB_CYCLE_32768 = 7,
+ }
+ #[doc = "Clear the instruction pointer which is internally saved pointer by JMP_ON_CS. Refer Programmable Sequence Engine for details."]
+ CLRINSTRPTR start(31) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Flash Control Register 4"]
+ pub FLSHCR4<u32> RW [
+ #[doc = "Write mask option bit 1. This option bit could be used to remove AHB write burst start address alignment limitation."]
+ WMOPT1 start(0) width(1) RW {}
+ #[doc = "Write mask option bit 2. When using AP memory, This option bit could be used to remove AHB write burst minimal length limitation. When using this bit, WMOPT1 should also be set."]
+ WMOPT2 start(1) width(1) RW {}
+ #[doc = "Write mask enable bit for flash device on port A. When write mask function is needed for memory device on port A, this bit must be set."]
+ WMENA start(2) width(1) RW {}
+ #[doc = "Write mask enable bit for flash device on port B. When write mask function is needed for memory device on port B, this bit must be set."]
+ WMENB start(3) width(1) RW {}
+ #[doc = "Enable APMEM 16 bit write mask function, bit 9 for A1-B1 pair, bit 10 for A2-B2 pair."]
+ PAR_WM start(9) width(2) RW {}
+ #[doc = "Disable the address shift logic for lower density of 16 bit PSRAM."]
+ PAR_ADDR_ADJ_DIS start(11) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "IP Control Register 0"]
+ pub IPCR0<u32> RW []
+}
+
+ral_registers::register! {
+ #[doc = "IP Control Register 1"]
+ pub IPCR1<u32> RW [
+ #[doc = "Flash Read/Program Data Size (in Bytes) for IP command."]
+ IDATSZ start(0) width(16) RW {}
+ #[doc = "Sequence Index in LUT for IP command."]
+ ISEQID start(16) width(4) RW {}
+ #[doc = "Sequence Number for IP command. Zero is the first sequence."]
+ ISEQNUM start(24) width(3) RW {}
+ #[doc = "Parallel mode Enabled for IP command."]
+ IPAREN start(31) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "IP Command Register"]
+ pub IPCMD<u32> RW [
+ #[doc = "Setting this bit will trigger an IP Command."]
+ TRG start(0) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Status Register 0"]
+ pub STS0<u32> RO [
+ #[doc = "This status bit indicates the state machine in SEQ_CTL is idle and there is command sequence executing on FlexSPI interface."]
+ SEQIDLE start(0) width(1) RO {}
+ #[doc = "This status bit indicates the state machine in ARB_CTL is busy and there is command sequence granted by arbitrator and not finished yet on FlexSPI interface. When ARB_CTL state (ARBIDLE=0x1) is idle, there will be no transaction on FlexSPI interface also (SEQIDLE=0x1). So this bit should be polled to wait for FlexSPI controller become idle instead of SEQIDLE."]
+ ARBIDLE start(1) width(1) RO {}
+ #[doc = "This status field indicates the trigger source of current command sequence granted by arbitrator. This field value is meaningless when ARB_CTL is not busy (STS0[ARBIDLE]=0x1)."]
+ ARBCMDSRC start(2) width(2) RO {
+ #[doc = "Triggered by AHB read command (triggered by AHB read)."]
+ AHB_READ = 0,
+ #[doc = "Triggered by AHB write command (triggered by AHB Write)."]
+ AHB_WRITE = 1,
+ #[doc = "Triggered by IP command (triggered by setting register bit IPCMD.TRG)."]
+ IP_COMMAND = 2,
+ #[doc = "Triggered by suspended command (resumed)."]
+ SUSPEND = 3,
+ }
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Status Register 1"]
+ pub STS1<u32> RO [
+ #[doc = "Indicates the sequence index when an AHB command error is detected. This field will be cleared when INTR[AHBCMDERR] is write-1-clear(w1c)."]
+ AHBCMDERRID start(0) width(4) RO {}
+ #[doc = "Indicates the Error Code when AHB command Error detected. This field will be cleared when INTR[AHBCMDERR] is write-1-clear(w1c)."]
+ AHBCMDERRCODE start(8) width(4) RO {
+ #[doc = "No error."]
+ NONE = 0,
+ #[doc = "AHB Write command with JMP_ON_CS instruction used in the sequence."]
+ WRITE_WITH_JUMP_ON_CS = 2,
+ #[doc = "There is unknown instruction opcode in the sequence."]
+ UNKNOWN_OPCODE = 3,
+ #[doc = "Instruction DUMMY_SDR/DUMMY_RWDS_SDR used in DDR sequence."]
+ DUMMY_SDR_IN_DDR = 4,
+ #[doc = "Instruction DUMMY_DDR/DUMMY_RWDS_DDR used in SDR sequence."]
+ DUMMY_DDR_IN_SDR = 5,
+ #[doc = "Sequence execution timeout."]
+ TIMEOUT = 14,
+ }
+ #[doc = "Indicates the sequence Index when IP command error detected. This field will be cleared when INTR[IPCMDERR] is write-1-clear(w1c)."]
+ IPCMDERRID start(16) width(4) RO {}
+ #[doc = "Indicates the Error Code when IP command Error detected. This field will be cleared when INTR[IPCMDERR] is write-1-clear(w1c)."]
+ IPCMDERRCODE start(24) width(4) RO {
+ #[doc = "No error."]
+ NONE = 0,
+ #[doc = "IP command with JMP_ON_CS instruction used in the sequence."]
+ JUMP_ON_CS = 2,
+ #[doc = "There is unknown instruction opcode in the sequence."]
+ UNKNOWN_OPCODE = 3,
+ #[doc = "Instruction DUMMY_SDR/DUMMY_RWDS_SDR used in DDR sequence."]
+ DUMMY_SDR_IN_DDR = 4,
+ #[doc = "Instruction DUMMY_DDR/DUMMY_RWDS_DDR used in SDR sequence."]
+ DUMMY_DDR_IN_SDR = 5,
+ #[doc = "Flash access start address exceed the whole flash address range (A1/A2/B1/B2)."]
+ OUT_OF_RANGE = 6,
+ #[doc = "Sequence execution timeout."]
+ TIMEOUT = 14,
+ #[doc = "Flash boundary crossed."]
+ BOUNDARY_CROSS = 15,
+ }
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Status Register 2"]
+ pub STS2<u32> RO [
+ #[doc = "Flash A sample clock slave delay line locked."]
+ ASLVLOCK start(0) width(1) RO {}
+ #[doc = "Flash A sample clock reference delay line locked."]
+ AREFLOCK start(1) width(1) RO {}
+ #[doc = "Flash A sample clock slave delay line delay cell number selection ."]
+ ASLVSEL start(2) width(6) RO {}
+ #[doc = "Flash A sample clock reference delay line delay cell number selection."]
+ AREFSEL start(8) width(6) RO {}
+ #[doc = "Flash B sample clock slave delay line locked."]
+ BSLVLOCK start(16) width(1) RO {}
+ #[doc = "Flash B sample clock reference delay line locked."]
+ BREFLOCK start(17) width(1) RO {}
+ #[doc = "Flash B sample clock slave delay line delay cell number selection."]
+ BSLVSEL start(18) width(6) RO {}
+ #[doc = "Flash B sample clock reference delay line delay cell number selection."]
+ BREFSEL start(24) width(6) RO {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "IP RX FIFO Status Register"]
+ pub IPRXFSTS<u32> RO [
+ #[doc = "Fill level of IP RX FIFO."]
+ FILL start(0) width(8) RO {}
+ #[doc = "Total Read Data Counter: RDCNTR * 64 Bits."]
+ RDCNTR start(16) width(16) RO {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "IP TX FIFO Status Register"]
+ pub IPTXFSTS<u32> RO [
+ #[doc = "Fill level of IP TX FIFO."]
+ FILL start(0) width(8) RO {}
+ #[doc = "Total Write Data Counter: WRCNTR * 64 Bits."]
+ WRCNTR start(16) width(16) RO {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "IP RX FIFO Control Register"]
+ pub IPRXFCR<u32> RW [
+ #[doc = "Clear all valid data entries in IP RX FIFO."]
+ CLRIPRXF start(0) width(1) RW {}
+ #[doc = "IP RX FIFO reading by DMA enabled."]
+ RXDMAEN start(1) width(1) RW {
+ #[doc = "IP RX FIFO would be read by processor."]
+ CPU = 0,
+ #[doc = "IP RX FIFO would be read by DMA."]
+ DMA = 1,
+ }
+ #[doc = "Watermark level is (RXWMRK+1)*64 Bits."]
+ RXWMRK start(2) width(5) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "IP TX FIFO Control Register"]
+ pub IPTXFCR<u32> RW [
+ #[doc = "Clear all valid data entries in IP TX FIFO."]
+ CLRIPTXF start(0) width(1) RW {}
+ #[doc = "IP TX FIFO filling by DMA enabled."]
+ TXDMAEN start(1) width(1) RW {
+ #[doc = "IP TX FIFO would be filled by processor."]
+ CPU = 0,
+ #[doc = "IP TX FIFO would be filled by DMA."]
+ DMA = 1,
+ }
+ #[doc = "Watermark level is (TXWMRK+1)*64 Bits."]
+ TXWMRK start(2) width(5) RW {}
+ ]
+}
+
+ral_registers::register! {
+ pub RFDR<u32> RO []
+}
+
+ral_registers::register! {
+ pub TFDR<u32> WO []
+}
+
+#[cfg(test)]
+mod tests {
+ use core::mem::MaybeUninit;
+
+ use ral_registers::Instance;
+
+ use crate::{Instr, SeqId, SeqIdRange, Sequence};
+
+ use super::RegisterBlock;
+
+ #[test]
+ fn layout_test() {
+ use core::mem::offset_of;
+ assert_eq!(offset_of!(RegisterBlock, FLSHCR4), 0x94);
+ assert_eq!(offset_of!(RegisterBlock, IPCR0), 0xA0);
+ assert_eq!(offset_of!(RegisterBlock, STS0), 0xE0);
+ assert_eq!(offset_of!(RegisterBlock, AHBSPNDSTS), 0xEC);
+ assert_eq!(offset_of!(RegisterBlock, RFDR), 0x100);
+ assert_eq!(offset_of!(RegisterBlock, TFDR), 0x180);
+ assert_eq!(offset_of!(RegisterBlock, LUT), 0x200);
+ }
+
+ #[test]
+ fn set_sequence() {
+ let mut flexspi: RegisterBlock = unsafe { MaybeUninit::zeroed().assume_init() };
+ let flexspi = unsafe { Instance::new_unchecked(&mut flexspi) };
+ super::set_sequence(flexspi, SeqId::Seq04, &Sequence([Instr::STOP; _]));
+ }
+
+ #[test]
+ fn seq_id_start_range() {
+ let mut range = SeqIdRange::start(SeqId::Seq13);
+ assert_eq!(range.next(), Some(SeqId::Seq13));
+ assert_eq!(range.next(), Some(SeqId::Seq14));
+ assert_eq!(range.next(), Some(SeqId::Seq15));
+ assert_eq!(range.next(), None);
+ assert_eq!(range.next(), None);
+ assert_eq!(range.next(), None);
+ assert_eq!(range.next(), None);
+ }
+}
diff --git a/drivers/gpc-11xx/Cargo.toml b/drivers/gpc-11xx/Cargo.toml
new file mode 100644
index 0000000..65ded0f
--- /dev/null
+++ b/drivers/gpc-11xx/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "imxrt-drivers-gpc-11xx"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+ral-registers = { workspace = true }
diff --git a/drivers/gpc-11xx/src/lib.rs b/drivers/gpc-11xx/src/lib.rs
new file mode 100644
index 0000000..847bb50
--- /dev/null
+++ b/drivers/gpc-11xx/src/lib.rs
@@ -0,0 +1,381 @@
+#![no_std]
+
+pub mod cpu_mode_ctrl {
+ pub type Instance = ral_registers::Instance<RegisterBlock>;
+
+ #[repr(C)]
+ #[allow(non_snake_case)]
+ pub struct RegisterBlock {
+ reserved_0: [u8; 4],
+ pub CM_AUTHEN_CTRL: u32,
+ pub CM_INT_CTRL: u32,
+ pub CM_MISC: u32,
+ pub CM_MODE_CTRL: u32,
+ pub CM_MODE_STAT: u32,
+ reserved_1: [u8; 232],
+ pub CM_IRQ_WAKEUP_MASK: [u32; 8],
+ reserved_2: [u8; 32],
+ pub CM_NON_IRQ_WAKEUP_MASK: u32,
+ reserved_3: [u8; 12],
+ pub CM_IRQ_WAKEUP_STAT: [u32; 8],
+ reserved_4: [u8; 32],
+ pub CM_NON_IRQ_WAKEUP_STAT: u32,
+ reserved_5: [u8; 108],
+ pub CM_SLEEP_SSAR_CTRL: u32,
+ reserved_6: [u8; 4],
+ pub CM_SLEEP_LPCG_CTRL: u32,
+ reserved_7: [u8; 4],
+ pub CM_SLEEP_PLL_CTRL: u32,
+ reserved_8: [u8; 4],
+ pub CM_SLEEP_ISO_CTRL: u32,
+ reserved_9: [u8; 4],
+ pub CM_SLEEP_RESET_CTRL: u32,
+ reserved_10: [u8; 4],
+ pub CM_SLEEP_POWER_CTRL: u32,
+ reserved_11: [u8; 100],
+ pub CM_WAKEUP_POWER_CTRL: u32,
+ reserved_12: [u8; 4],
+ pub CM_WAKEUP_RESET_CTRL: u32,
+ reserved_13: [u8; 4],
+ pub CM_WAKEUP_ISO_CTRL: u32,
+ reserved_14: [u8; 4],
+ pub CM_WAKEUP_PLL_CTRL: u32,
+ reserved_15: [u8; 4],
+ pub CM_WAKEUP_LPCG_CTRL: u32,
+ reserved_16: [u8; 4],
+ pub CM_WAKEUP_SSAR_CTRL: u32,
+ reserved_17: [u8; 68],
+ pub CM_SP_CTRL: u32,
+ pub CM_SP_STAT: u32,
+ reserved_18: [u8; 8],
+ pub CM_RUN_MODE_MAPPING: u32,
+ pub CM_WAIT_MODE_MAPPING: u32,
+ pub CM_STOP_MODE_MAPPING: u32,
+ pub CM_SUSPEND_MODE_MAPPING: u32,
+ pub CM_SP_MAPPING: [u32; 16],
+ reserved_19: [u8; 32],
+ pub CM_STBY_CTRL: u32,
+ }
+
+ ral_registers::register! {
+ #[doc = "CM Authentication Control"]
+ pub CM_AUTHEN_CTRL<u32> RW [
+ #[doc = "Allow user mode access"]
+ USER start(0) width(1) RW {}
+ #[doc = "Allow non-secure mode access"]
+ NONSECURE start(1) width(1) RW {}
+ #[doc = "Lock NONSECURE and USER"]
+ LOCK_SETTING start(4) width(1) RW {}
+ #[doc = "Domain ID white list"]
+ WHITE_LIST start(8) width(4) RW {}
+ #[doc = "White list lock"]
+ LOCK_LIST start(12) width(1) RW {}
+ #[doc = "Configuration lock"]
+ LOCK_CFG start(20) width(1) RW {}
+ ]
+ }
+
+ ral_registers::register! {
+ #[doc = "CM Interrupt Control"]
+ pub CM_INT_CTRL<u32> RW [
+ #[doc = "sp_req_not_allowed_for_sleep interrupt enable"]
+ SP_REQ_NOT_ALLOWED_SLEEP_INT_EN start(0) width(1) RW {}
+ #[doc = "sp_req_not_allowed_for_wakeup interrupt enable"]
+ SP_REQ_NOT_ALLOWED_WAKEUP_INT_EN start(1) width(1) RW {}
+ #[doc = "sp_req_not_allowed_for_soft interrupt enable"]
+ SP_REQ_NOT_ALLOWED_SOFT_INT_EN start(2) width(1) RW {}
+ #[doc = "sp_req_not_allowed_for_sleep interrupt status and clear register"]
+ SP_REQ_NOT_ALLOWED_SLEEP_INT start(16) width(1) RW {}
+ #[doc = "sp_req_not_allowed_for_wakeup interrupt status and clear register"]
+ SP_REQ_NOT_ALLOWED_WAKEUP_INT start(17) width(1) RW {}
+ #[doc = "sp_req_not_allowed_for_soft interrupt status and clear register"]
+ SP_REQ_NOT_ALLOWED_SOFT_INT start(18) width(1) RW {}
+ ]
+ }
+
+ ral_registers::register! {
+ #[doc = "Miscellaneous"]
+ pub CM_MISC<u32> RW [
+ #[doc = "Non-masked interrupt status"]
+ NMI_STAT start(0) width(1) RO {}
+ #[doc = "Allow cpu_sleep_hold_req assert during CPU low power status"]
+ SLEEP_HOLD_EN start(1) width(1) RW {}
+ #[doc = "Status of cpu_sleep_hold_ack_b"]
+ SLEEP_HOLD_STAT start(2) width(1) RO {}
+ #[doc = "Master CPU"]
+ MASTER_CPU start(4) width(1) RW {}
+ ]
+ }
+
+ ral_registers::register! {
+ #[doc = "CPU mode control"]
+ pub CM_MODE_CTRL<u32> RW [
+ #[doc = "The CPU mode the CPU platform should transit to on next sleep event"]
+ CPU_MODE_TARGET start(0) width(2) RW {
+ #[doc = "Stay in RUN mode"]
+ RUN = 0,
+ #[doc = "Transit to WAIT mode"]
+ WAIT = 0x1,
+ #[doc = "Transit to STOP mode"]
+ STOP = 0x2,
+ #[doc = "Transit to SUSPEND mode"]
+ SUSPEND = 0x3,
+ }
+ #[doc = "WFE assertion can be sleep event"]
+ WFE_EN start(4) width(1) RW {}
+ ]
+ }
+
+ ral_registers::register! {
+ #[doc = "CM CPU mode Status"]
+ pub CM_MODE_STAT<u32> RO [
+ #[doc = "Current CPU mode"]
+ CPU_MODE_CURRENT start(0) width(2) RO {
+ #[doc = "CPU is currently in RUN mode"]
+ RUN = 0,
+ #[doc = "CPU is currently in WAIT mode"]
+ WAIT = 0x1,
+ #[doc = "CPU is currently in STOP mode"]
+ STOP = 0x2,
+ #[doc = "CPU is currently in SUSPEND mode"]
+ SUSPEND = 0x3,
+ }
+ #[doc = "Previous CPU mode"]
+ CPU_MODE_PREVIOUS start(2) width(2) RO {
+ #[doc = "CPU was previously in RUN mode"]
+ RUN = 0,
+ #[doc = "CPU was previously in WAIT mode"]
+ WAIT = 0x1,
+ #[doc = "CPU was previously in STOP mode"]
+ STOP = 0x2,
+ #[doc = "CPU was previously in SUSPEND mode"]
+ SUSPEND = 0x3,
+ }
+ ]
+ }
+
+ ral_registers::register! {
+ #[doc = "CM IRQ0~31 wakeup mask"]
+ pub CM_IRQ_WAKEUP_MASK<u32> RW []
+ }
+
+ ral_registers::register! {
+ #[doc = "CM non-irq wakeup mask"]
+ pub CM_NON_IRQ_WAKEUP_MASK<u32> RW [
+ #[doc = "There are 256 interrupts and 1 event as a wakeup source for GPC. This field masks the 1 event wakeup source."]
+ EVENT_WAKEUP_MASK start(0) width(1) RW {}
+ #[doc = "1 means the debug_wakeup_request cannot wakeup CPU platform"]
+ DEBUG_WAKEUP_MASK start(1) width(1) RW {}
+ ]
+ }
+
+ ral_registers::register! {
+ #[doc = "CM IRQ0~31 wakeup status"]
+ pub CM_IRQ_WAKEUP_STAT<u32> RO []
+ }
+
+ ral_registers::register! {
+ #[doc = "CM non-irq wakeup status"]
+ pub CM_NON_IRQ_WAKEUP_STAT<u32> RO [
+ #[doc = "Event wakeup status"]
+ EVENT_WAKEUP_STAT start(0) width(1) RO {}
+ #[doc = "Debug wakeup status"]
+ DEBUG_WAKEUP_STAT start(1) width(1) RO {}
+ ]
+ }
+
+ ral_registers::register! {
+ #[doc(hidden)]
+ pub CM_SLEEP_WAKEUP_CTRL<u32> RW [
+ #[doc = "Step count, useage is depending on CNT_MODE."]
+ STEP_CNT start(0) width(16) RW {}
+ #[doc = "Count mode"]
+ CNT_MODE start(28) width(2) RW {
+ #[doc = "Counter disable mode: not use step counter, step completes once receiving step_done"]
+ DISABLE = 0,
+ #[doc = "Counter delay mode: delay after receiving step_done, delay cycle number is STEP_CNT"]
+ DELAY = 0x1,
+ #[doc = "Ignore step_done response, the counter starts to count once step begins, when counter reaches STEP_CNT value, the step completes"]
+ IGNORE_STEP_DONE = 0x2,
+ #[doc = "Time out mode, the counter starts to count once step begins, the step completes when either step_done received or counting to STEP_CNT value"]
+ TIMEOUT = 0x3,
+ }
+ #[doc = "Disable this step"]
+ DISABLE start(31) width(1) RW {}
+ ]
+ }
+
+ use core::num::NonZeroU32;
+
+ #[doc(inline)]
+ pub use CM_SLEEP_WAKEUP_CTRL as CM_SLEEP_LPGC_CTRL;
+ #[doc(inline)]
+ pub use CM_SLEEP_WAKEUP_CTRL as CM_SLEEP_PLL_CTRL;
+ #[doc(inline)]
+ pub use CM_SLEEP_WAKEUP_CTRL as CM_SLEEP_ISO_CTRL;
+ #[doc(inline)]
+ pub use CM_SLEEP_WAKEUP_CTRL as CM_SLEEP_RESET_CTRL;
+ #[doc(inline)]
+ pub use CM_SLEEP_WAKEUP_CTRL as CM_SLEEP_POWER_CTRL;
+ #[doc(inline)]
+ pub use CM_SLEEP_WAKEUP_CTRL as CM_SLEEP_SSAR_CTRL;
+
+ #[doc(inline)]
+ pub use CM_SLEEP_WAKEUP_CTRL as CM_WAKEUP_LPGC_CTRL;
+ #[doc(inline)]
+ pub use CM_SLEEP_WAKEUP_CTRL as CM_WAKEUP_PLL_CTRL;
+ #[doc(inline)]
+ pub use CM_SLEEP_WAKEUP_CTRL as CM_WAKEUP_ISO_CTRL;
+ #[doc(inline)]
+ pub use CM_SLEEP_WAKEUP_CTRL as CM_WAKEUP_RESET_CTRL;
+ #[doc(inline)]
+ pub use CM_SLEEP_WAKEUP_CTRL as CM_WAKEUP_POWER_CTRL;
+ #[doc(inline)]
+ pub use CM_SLEEP_WAKEUP_CTRL as CM_WAKEUP_SSAR_CTRL;
+
+ ral_registers::register! {
+ #[doc = "CM Setpoint Control"]
+ pub CM_SP_CTRL<u32> RW [
+ #[doc = "Request a Setpoint transition when this bit is set"]
+ RUN_EN start(0) width(1) RW {}
+ #[doc = "The Setpoint that CPU want the system to transit to when CPU_SP_RUN_EN is set"]
+ RUN start(1) width(4) RW {}
+ #[doc = "1 means enable Setpoint transition on next CPU platform sleep sequence"]
+ SLEEP_EN start(5) width(1) RW {}
+ #[doc = "The Setpoint that CPU want the system to transit to on next CPU platform sleep sequence"]
+ SLEEP start(6) width(4) RW {}
+ #[doc = "1 means enable Setpoint transition on next CPU platform wakeup sequence"]
+ WAKEUP_EN start(10) width(1) RW {}
+ #[doc = "The Setpoint that CPU want the system to transit to on next CPU platform wakeup sequence"]
+ WAKEUP start(11) width(4) RW {}
+ #[doc = "Select the Setpoint transiton on the next CPU platform wakeup sequence"]
+ WAKEUP_SEL start(15) width(1) RW {}
+ ]
+ }
+
+ ral_registers::register! {
+ #[doc = "CM Setpoint Status"]
+ pub CM_SP_STAT<u32> RO [
+ #[doc = "The current Setpoint of the system"]
+ CURRENT start(0) width(4) RO {}
+ #[doc = "The previous Setpoint of the system"]
+ PREVIOUS start(4) width(4) RO {}
+ #[doc = "The requested Setpoint from the CPU platform"]
+ TARGET start(8) width(4) RO {}
+ ]
+ }
+
+ ral_registers::register! {
+ #[doc(hidden)]
+ pub CM_MAPPING<u32> RW [
+ #[doc = "Defines which Setpoint is allowed when CPU enters this mode. Each bit stands for 1 Setpoint, locked by LOCK_CFG field"]
+ CPU_MAPPING start(0) width(16) RW {}
+ ]
+ }
+
+ #[doc(inline)]
+ pub use CM_MAPPING as CM_RUN_MODE_MAPPING;
+ #[doc(inline)]
+ pub use CM_MAPPING as CM_WAIT_MODE_MAPPING;
+ #[doc(inline)]
+ pub use CM_MAPPING as CM_STOP_MODE_MAPPING;
+ #[doc(inline)]
+ pub use CM_MAPPING as CM_SUSUPEND_MODE_MAPPING;
+ #[doc(inline)]
+ pub use CM_MAPPING as CM_SP_MAPPING;
+
+ ral_registers::register! {
+ #[doc = "CM standby control"]
+ pub CM_STBY_CTRL<u32> RW [
+ #[doc = "0x1: Request the chip into standby mode when CPU entering WAIT mode, locked by LOCK_CFG field."]
+ WAIT start(0) width(1) RW {}
+ #[doc = "0x1: Request the chip into standby mode when CPU entering STOP mode, locked by LOCK_CFG field."]
+ STOP start(1) width(1) RW {}
+ #[doc = "0x1: Request the chip into standby mode when CPU entering SUSPEND mode, locked by LOCK_CFG field."]
+ SUSPEND start(2) width(1) RW {}
+ #[doc = "Indicate the CPU is busy entering standby mode."]
+ SLEEP_BUSY start(16) width(1) RO {}
+ #[doc = "Indicate the CPU is busy exiting standby mode."]
+ WAKEUP_BUSY start(17) width(1) RO {}
+ ]
+ }
+
+ /// The wrapped setpoint is invalid.
+ #[derive(Clone, Copy, PartialEq, Eq)]
+ #[repr(transparent)]
+ pub struct InvalidSetpointError(NonZeroU32);
+ // Amenable to niche optimizations in a Result, Option.
+
+ impl InvalidSetpointError {
+ /// Returns the invalid setpoint that you specified.
+ #[inline]
+ #[must_use]
+ pub const fn which(self) -> u32 {
+ self.0.get() - 1
+ }
+
+ /// Produce an error from a user-supplied invalid setpoint.
+ #[must_use]
+ #[inline]
+ fn from_raw(setpoint: u32) -> Self {
+ // Safety: any setpoint plus one is naturally
+ // non-zero. Saturating add prevents wrap around,
+ // ignoring the possibility that the user supplied
+ // a very invalid setpoint.
+ Self(unsafe { NonZeroU32::new_unchecked(setpoint.saturating_add(1)) })
+ }
+ }
+
+ /// Check if a setpoint is valid.
+ pub fn check_setpoint(setpoint: u32) -> Result<(), InvalidSetpointError> {
+ if setpoint < 16 {
+ Ok(())
+ } else {
+ Err(InvalidSetpointError::from_raw(setpoint))
+ }
+ }
+
+ impl core::fmt::Debug for InvalidSetpointError {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ f.debug_tuple("InvalidSetpointError")
+ .field(&self.which())
+ .finish()
+ }
+ }
+
+ impl core::fmt::Display for InvalidSetpointError {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ write!(f, "Invalid setpoint: {}", self.which())
+ }
+ }
+
+ /// Ask the GPC for a setpoint transition, blocking until
+ /// the request concludes.
+ ///
+ /// Your setpoint must be less than 16. Otherwise, this call
+ /// returns an error.
+ pub fn request_setpoint_transition(
+ gpc: Instance,
+ setpoint: u32,
+ ) -> Result<(), InvalidSetpointError> {
+ check_setpoint(setpoint)?;
+ ral_registers::modify_reg!(self, gpc, CM_SP_CTRL, RUN: setpoint as u32, RUN_EN: 1);
+ while ral_registers::read_reg!(self, gpc, CM_SP_CTRL, RUN_EN == 1) {}
+ Ok(())
+ }
+
+ #[cfg(test)]
+ mod tests {
+ #[test]
+ fn layout() {
+ assert_eq!(
+ core::mem::offset_of!(super::RegisterBlock, CM_SP_CTRL),
+ 0x300
+ );
+ assert_eq!(
+ core::mem::offset_of!(super::RegisterBlock, CM_STBY_CTRL),
+ 0x380
+ );
+ }
+ }
+}
diff --git a/drivers/gpio/Cargo.toml b/drivers/gpio/Cargo.toml
new file mode 100644
index 0000000..9e7f4e1
--- /dev/null
+++ b/drivers/gpio/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "imxrt-drivers-gpio"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+ral-registers = { workspace = true }
diff --git a/drivers/gpio/src/lib.rs b/drivers/gpio/src/lib.rs
new file mode 100644
index 0000000..31a04b1
--- /dev/null
+++ b/drivers/gpio/src/lib.rs
@@ -0,0 +1,34 @@
+#![no_std]
+
+use ral_registers::register;
+
+#[allow(non_snake_case)]
+#[repr(C)]
+pub struct RegisterBlock {
+ pub DR: u32,
+ pub GDIR: u32,
+ pub PSR: u32,
+ pub ICR1: u32,
+ pub ICR2: u32,
+ pub IMR: u32,
+ pub ISR: u32,
+ pub EDGE_SEL: u32,
+ _reserved0: [u8; 0x64],
+ pub DR_SET: u32,
+ pub DR_CLEAR: u32,
+ pub DR_TOGGLE: u32,
+}
+
+pub type GPIO = ral_registers::Instance<RegisterBlock>;
+
+register!(pub DR<u32> RW []);
+register!(pub GDIR<u32> RW []);
+register!(pub PSR<u32> RO []);
+register!(pub ICR1<u32> RW []);
+register!(pub ICR2<u32> RW []);
+register!(pub IMR<u32> RW []);
+register!(pub ISR<u32> RW []);
+register!(pub EDGE_SEL<u32> RW []);
+register!(pub DR_SET<u32> WO []);
+register!(pub DR_CLEAR<u32> WO []);
+register!(pub DR_TOGGLE<u32> WO []);
diff --git a/drivers/iomuxc-10xx/Cargo.toml b/drivers/iomuxc-10xx/Cargo.toml
new file mode 100644
index 0000000..23e252d
--- /dev/null
+++ b/drivers/iomuxc-10xx/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "imxrt-drivers-iomuxc-10xx"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+ral-registers = { workspace = true }
diff --git a/drivers/iomuxc-10xx/src/lib.rs b/drivers/iomuxc-10xx/src/lib.rs
new file mode 100644
index 0000000..b144065
--- /dev/null
+++ b/drivers/iomuxc-10xx/src/lib.rs
@@ -0,0 +1,102 @@
+#![no_std]
+
+pub mod iomuxc_gpr {
+ #[repr(C)]
+ #[allow(non_snake_case)]
+ pub struct RegisterBlock<const GPR_REGISTERS: usize> {
+ pub GPR: [u32; GPR_REGISTERS],
+ }
+
+ ral_registers::register!(pub GPR<u32> RW []);
+}
+
+pub mod iomuxc {
+ #[repr(C)]
+ #[allow(non_snake_case)]
+ pub struct RegisterBlock<
+ const OFFSET_BYTES: usize,
+ const PAD_REGISTERS: usize,
+ const SELECT_INPUT: usize,
+ > {
+ _reserved: [u8; OFFSET_BYTES],
+ pub SW_MUX_CTL_PAD: [u32; PAD_REGISTERS],
+ pub SW_PAD_CTL_PAD: [u32; PAD_REGISTERS],
+ pub SELECT_INPUT: [u32; SELECT_INPUT],
+ }
+
+ ral_registers::register! {
+ /// Multiplex control register.
+ pub SW_MUX_CTL_PAD<u32> RW [
+ /// Multiplex selection.
+ ///
+ /// Note that this bitwidth may be larger than
+ /// what is implemented for your IP. Consult
+ /// your part's reference manual for more information.
+ MUX_MODE start(0) width(4) RW {}
+ /// Software Input On.
+ SION start(4) width(1) RW {}
+ ]}
+
+ ral_registers::register! {
+ /// Pad control register.
+ pub SW_PAD_CTL_PAD<u32> RW [
+ /// Slew rate.
+ SRE start(0) width(1) RW {
+ /// Slow slew rate.
+ SLOW = 0,
+ /// Fast slew rate.
+ FAST = 1,
+ }
+
+ /// Drive strength.
+ DSE start(3) width(3) RW {
+ DISABLED = 0,
+ R0 = 1,
+ R0_2 = 2,
+ R0_3 = 3,
+ R0_4 = 4,
+ R0_5 = 5,
+ R0_6 = 6,
+ R0_7 = 7,
+ }
+
+ /// Speed.
+ SPEED start(6) width(2) RW {
+ LOW_50MHZ = 0,
+ MEDIUM_100MHz = 1,
+ FAST_150MHZ = 2,
+ MAX_200MHZ = 3,
+ }
+
+ /// Open drain enable.
+ ODE start(11) width(1) RW {}
+ /// Pull / keep enable.
+ PKE start(12) width(1) RW {}
+ /// Pull / keep select
+ PUE start(13) width(1) RW {
+ /// Use the keeper.
+ KEEPER = 0,
+ /// Use the PU / PD.
+ PULL = 1,
+ }
+ /// Pull up / down selection.
+ PUS start(14) width(2) RW {
+ /// 100K Ohm Pull Down
+ PD_100K_OHM = 0,
+ /// 47K Ohm Pull Up
+ PU_47K_OHM = 0x01,
+ /// 100K Ohm Pull Up
+ PU_100K_OHM = 0x02,
+ /// 22K Ohm Pull Up
+ PU_22K_OHM = 0x03,
+ }
+
+ /// Hysteresis enable.
+ HYS start(16) width(1) RW {}
+ ]
+ }
+
+ ral_registers::register! {
+ pub SELECT_INPUT<u32> RW []
+ }
+}
diff --git a/drivers/iomuxc-11xx/Cargo.toml b/drivers/iomuxc-11xx/Cargo.toml
new file mode 100644
index 0000000..90c607a
--- /dev/null
+++ b/drivers/iomuxc-11xx/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "imxrt-drivers-iomuxc-11xx"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+ral-registers = { workspace = true }
diff --git a/drivers/iomuxc-11xx/src/lib.rs b/drivers/iomuxc-11xx/src/lib.rs
new file mode 100644
index 0000000..54f0c17
--- /dev/null
+++ b/drivers/iomuxc-11xx/src/lib.rs
@@ -0,0 +1,475 @@
+#![no_std]
+
+pub mod iomuxc_gpr {
+ #[repr(C)]
+ #[allow(non_snake_case)]
+ pub struct RegisterBlock {
+ pub GPR: [u32; 76],
+ }
+
+ ral_registers::register!(pub GPR<u32> RW []);
+}
+
+pub mod iomuxc {
+ #[repr(C)]
+ #[allow(non_snake_case)]
+ pub struct RegisterBlock {
+ _reserved: [u8; 16],
+ pub SW_MUX_CTL_PAD: PadLayout,
+ pub SW_PAD_CTL_PAD: PadLayout,
+ pub SELECT_INPUT: [u32; 160],
+ }
+
+ #[doc(hidden)]
+ #[repr(C)]
+ #[allow(non_snake_case)]
+ pub struct PadLayout {
+ pub GPIO_EMC_B1: [u32; 42],
+ pub GPIO_EMC_B2: [u32; 21],
+ pub GPIO_AD: [u32; 36],
+ pub GPIO_SD_B1: [u32; 6],
+ pub GPIO_SD_B2: [u32; 12],
+ pub GPIO_DISP_B1: [u32; 12],
+ pub GPIO_DISP_B2: [u32; 16],
+ }
+
+ const _: () = assert!(size_of::<PadLayout>() == size_of::<[u32; 145]>());
+
+ #[allow(non_snake_case)]
+ pub mod SW_PAD_CTL_PAD {
+ #[doc(inline)]
+ pub use super::PadLayout as RegisterBlock;
+
+ ral_registers::register! {
+ #[doc(hidden)]
+ pub v1<u32> RW [
+ #[doc = "PDRV Field"]
+ PDRV start(1) width(1) RW {
+ HIGH = 0
+ NORMAL = 0x1,
+ }
+ #[doc = "Pull Down Pull Up Field"]
+ PULL start(2) width(2) RW {
+ PULL_UP = 0x1,
+ PULL_DOWN = 0x2,
+ NO_PULL = 0x3,
+ }
+ #[doc = "Open Drain Field"]
+ ODE start(4) width(1) RW {}
+ #[doc = "Domain write protection"]
+ DWP start(28) width(2) RW {}
+ #[doc = "Domain write protection lock"]
+ DWP_LOCK start(30) width(2) RW {}
+ ]
+ }
+
+ ral_registers::register! {
+ #[doc(hidden)]
+ pub v2<u32> RW [
+ #[doc = "Slew Rate Field"]
+ SRE start(0) width(1) RW {
+ SLOW = 0,
+ FAST = 0x1,
+ }
+ #[doc = "Drive Strength Field"]
+ DSE start(1) width(1) RW {
+ NORMAL = 0,
+ HIGH = 0x1,
+ }
+ #[doc = "Pull / Keep Select Field"]
+ PUE start(2) width(1) RW {}
+ #[doc = "Pull Up / Down Config. Field"]
+ PUS start(3) width(1) RW {
+ PULL_DOWN = 0,
+ PULL_UP = 0x1,
+ }
+ #[doc = "Open Drain Field"]
+ ODE start(4) width(1) RW {}
+ #[doc = "Domain write protection"]
+ DWP start(28) width(2) RW {}
+ #[doc = "Domain write protection lock"]
+ DWP_LOCK start(30) width(2) RW {}
+ ]
+ }
+
+ #[doc(inline)]
+ pub use v1 as GPIO_EMC_B1;
+ #[doc(inline)]
+ pub use v1 as GPIO_EMC_B2;
+ #[doc(inline)]
+ pub use v1 as GPIO_SD_B1;
+ #[doc(inline)]
+ pub use v1 as GPIO_SD_B2;
+ #[doc(inline)]
+ pub use v1 as GPIO_DISP_B1;
+
+ #[doc(inline)]
+ pub use v2 as GPIO_AD;
+ #[doc(inline)]
+ pub use v2 as GPIO_DISP_B2;
+ }
+
+ #[allow(non_snake_case)]
+ pub mod SW_MUX_CTL_PAD {
+ #[doc(inline)]
+ pub use super::PadLayout as RegisterBlock;
+
+ ral_registers::register! {
+ #[doc(hidden)]
+ pub common<u32> RW [
+ #[doc = "MUX Mode Select Field."]
+ MUX_MODE start(0) width(4) RW {}
+ #[doc = "Software Input On Field."]
+ SION start(4) width(1) RW {}
+ ]
+ }
+
+ #[doc(inline)]
+ pub use common as GPIO_EMC_B1;
+ #[doc(inline)]
+ pub use common as GPIO_EMC_B2;
+ #[doc(inline)]
+ pub use common as GPIO_SD_B1;
+ #[doc(inline)]
+ pub use common as GPIO_SD_B2;
+ #[doc(inline)]
+ pub use common as GPIO_DISP_B1;
+ #[doc(inline)]
+ pub use common as GPIO_DISP_B2;
+ #[doc(inline)]
+ pub use common as GPIO_AD;
+ }
+
+ pub mod select_input {
+ pub const FLEXCAN1_RX: usize = 0;
+ pub const FLEXCAN2_RX: usize = 1;
+ pub const CCM_ENET_QOS_REF_CLK: usize = 2;
+ pub const CCM_ENET_QOS_TX_CLK: usize = 3;
+ pub const ENET_IPG_CLK_RMII: usize = 4;
+ pub const ENET_MAC0_MDIO: usize = 5;
+ pub const ENET_MAC0_RXDATA_0: usize = 6;
+ pub const ENET_MAC0_RXDATA_1: usize = 7;
+ pub const ENET_MAC0_RXEN: usize = 8;
+ pub const ENET_MAC0_RXERR: usize = 9;
+ pub const ENET_MAC0_TXCLK: usize = 10;
+ pub const ENET_1G_IPG_CLK_RMII: usize = 11;
+ pub const ENET_1G_MAC0_MDIO: usize = 12;
+ pub const ENET_1G_MAC0_RXCLK: usize = 13;
+ pub const ENET_1G_MAC0_RXDATA_0: usize = 14;
+ pub const ENET_1G_MAC0_RXDATA_1: usize = 15;
+ pub const ENET_1G_MAC0_RXDATA_2: usize = 16;
+ pub const ENET_1G_MAC0_RXDATA_3: usize = 17;
+ pub const ENET_1G_MAC0_RXEN: usize = 18;
+ pub const ENET_1G_MAC0_RXERR: usize = 19;
+ pub const ENET_1G_MAC0_TXCLK: usize = 20;
+ pub const ENET_QOS_GMII_MDI_I: usize = 21;
+ pub const ENET_QOS_PHY_RXD_I_0: usize = 22;
+ pub const ENET_QOS_PHY_RXD_I_1: usize = 23;
+ pub const ENET_QOS_PHY_RXDV_I: usize = 24;
+ pub const ENET_QOS_PHY_RXER_I: usize = 25;
+ pub const FLEXPWM1_PWMA_0: usize = 26;
+ pub const FLEXPWM1_PWMA_1: usize = 27;
+ pub const FLEXPWM1_PWMA_2: usize = 28;
+ pub const FLEXPWM1_PWMB_0: usize = 29;
+ pub const FLEXPWM1_PWMB_1: usize = 30;
+ pub const FLEXPWM1_PWMB_2: usize = 31;
+ pub const FLEXPWM2_PWMA_0: usize = 32;
+ pub const FLEXPWM2_PWMA_1: usize = 33;
+ pub const FLEXPWM2_PWMA_2: usize = 34;
+ pub const FLEXPWM2_PWMB_0: usize = 35;
+ pub const FLEXPWM2_PWMB_1: usize = 36;
+ pub const FLEXPWM2_PWMB_2: usize = 37;
+ pub const FLEXPWM3_PWMA_0: usize = 38;
+ pub const FLEXPWM3_PWMA_1: usize = 39;
+ pub const FLEXPWM3_PWMA_2: usize = 40;
+ pub const FLEXPWM3_PWMA_3: usize = 41;
+ pub const FLEXPWM3_PWMB_0: usize = 42;
+ pub const FLEXPWM3_PWMB_1: usize = 43;
+ pub const FLEXPWM3_PWMB_2: usize = 44;
+ pub const FLEXPWM3_PWMB_3: usize = 45;
+ pub const FLEXSPI1_I_DQS_FA: usize = 46;
+ pub const FLEXSPI1_I_IO_FA_0: usize = 47;
+ pub const FLEXSPI1_I_IO_FA_1: usize = 48;
+ pub const FLEXSPI1_I_IO_FA_2: usize = 49;
+ pub const FLEXSPI1_I_IO_FA_3: usize = 50;
+ pub const FLEXSPI1_I_IO_FB_0: usize = 51;
+ pub const FLEXSPI1_I_IO_FB_1: usize = 52;
+ pub const FLEXSPI1_I_IO_FB_2: usize = 53;
+ pub const FLEXSPI1_I_IO_FB_3: usize = 54;
+ pub const FLEXSPI1_I_SCK_FA: usize = 55;
+ pub const FLEXSPI1_I_SCK_FB: usize = 56;
+ pub const FLEXSPI2_I_IO_FA_0: usize = 57;
+ pub const FLEXSPI2_I_IO_FA_1: usize = 58;
+ pub const FLEXSPI2_I_IO_FA_2: usize = 59;
+ pub const FLEXSPI2_I_IO_FA_3: usize = 60;
+ pub const FLEXSPI2_I_SCK_FA: usize = 61;
+ pub const GPT3_CAPIN1: usize = 62;
+ pub const GPT3_CAPIN2: usize = 63;
+ pub const GPT3_CLKIN: usize = 64;
+ pub const KPP_COL_6: usize = 65;
+ pub const KPP_COL_7: usize = 66;
+ pub const KPP_ROW_6: usize = 67;
+ pub const KPP_ROW_7: usize = 68;
+ pub const LPI2C1_LPI2C_SCL: usize = 69;
+ pub const LPI2C1_LPI2C_SDA: usize = 70;
+ pub const LPI2C2_LPI2C_SCL: usize = 71;
+ pub const LPI2C2_LPI2C_SDA: usize = 72;
+ pub const LPI2C3_LPI2C_SCL: usize = 73;
+ pub const LPI2C3_LPI2C_SDA: usize = 74;
+ pub const LPI2C4_LPI2C_SCL: usize = 75;
+ pub const LPI2C4_LPI2C_SDA: usize = 76;
+ pub const LPSPI1_LPSPI_PCS_0: usize = 77;
+ pub const LPSPI1_LPSPI_SCK: usize = 78;
+ pub const LPSPI1_LPSPI_SDI: usize = 79;
+ pub const LPSPI1_LPSPI_SDO: usize = 80;
+ pub const LPSPI2_LPSPI_PCS_0: usize = 81;
+ pub const LPSPI2_LPSPI_PCS_1: usize = 82;
+ pub const LPSPI2_LPSPI_SCK: usize = 83;
+ pub const LPSPI2_LPSPI_SDI: usize = 84;
+ pub const LPSPI2_LPSPI_SDO: usize = 85;
+ pub const LPSPI3_LPSPI_PCS_0: usize = 86;
+ pub const LPSPI3_LPSPI_PCS_1: usize = 87;
+ pub const LPSPI3_LPSPI_PCS_2: usize = 88;
+ pub const LPSPI3_LPSPI_PCS_3: usize = 89;
+ pub const LPSPI3_LPSPI_SCK: usize = 90;
+ pub const LPSPI3_LPSPI_SDI: usize = 91;
+ pub const LPSPI3_LPSPI_SDO: usize = 92;
+ pub const LPSPI4_LPSPI_PCS_0: usize = 93;
+ pub const LPSPI4_LPSPI_SCK: usize = 94;
+ pub const LPSPI4_LPSPI_SDI: usize = 95;
+ pub const LPSPI4_LPSPI_SDO: usize = 96;
+ pub const LPUART1_LPUART_RXD: usize = 97;
+ pub const LPUART1_LPUART_TXD: usize = 98;
+ pub const LPUART10_LPUART_RXD: usize = 99;
+ pub const LPUART10_LPUART_TXD: usize = 100;
+ pub const LPUART7_LPUART_RXD: usize = 101;
+ pub const LPUART7_LPUART_TXD: usize = 102;
+ pub const LPUART8_LPUART_RXD: usize = 103;
+ pub const LPUART8_LPUART_TXD: usize = 104;
+ pub const QTIMER1_TMR0_INPUT: usize = 105;
+ pub const QTIMER1_TMR1_INPUT: usize = 106;
+ pub const QTIMER1_TMR2_INPUT: usize = 107;
+ pub const QTIMER2_TMR0_INPUT: usize = 108;
+ pub const QTIMER2_TMR1_INPUT: usize = 109;
+ pub const QTIMER2_TMR2_INPUT: usize = 110;
+ pub const QTIMER3_TMR0_INPUT: usize = 111;
+ pub const QTIMER3_TMR1_INPUT: usize = 112;
+ pub const QTIMER3_TMR2_INPUT: usize = 113;
+ pub const QTIMER4_TMR0_INPUT: usize = 114;
+ pub const QTIMER4_TMR1_INPUT: usize = 115;
+ pub const QTIMER4_TMR2_INPUT: usize = 116;
+ pub const SAI1_IPG_CLK_SAI_MCLK: usize = 117;
+ pub const SAI1_SAI_RXBCLK: usize = 118;
+ pub const SAI1_SAI_RXDATA_0: usize = 119;
+ pub const SAI1_SAI_RXSYNC: usize = 120;
+ pub const SAI1_SAI_TXBCLK: usize = 121;
+ pub const SAI1_SAI_TXSYNC: usize = 122;
+ pub const EMVSIM1_SIO: usize = 129;
+ pub const EMVSIM1_IPP_SIMPD: usize = 130;
+ pub const EMVSIM1_POWER_FAIL: usize = 131;
+ pub const EMVSIM2_SIO: usize = 132;
+ pub const EMVSIM2_IPP_SIMPD: usize = 133;
+ pub const EMVSIM2_POWER_FAIL: usize = 134;
+ pub const SPDIF_SPDIF_IN1: usize = 135;
+ pub const USB_OTG2_OC: usize = 136;
+ pub const USB_OTG_OC: usize = 137;
+ pub const USBPHY1_USB_ID: usize = 138;
+ pub const USBPHY2_USB_ID: usize = 139;
+ pub const USDHC1_IPP_CARD_DET: usize = 140;
+ pub const USDHC1_IPP_WP_ON: usize = 141;
+ pub const USDHC2_IPP_CARD_DET: usize = 142;
+ pub const USDHC2_IPP_WP_ON: usize = 143;
+ pub const XBAR1_IN_20: usize = 144;
+ pub const XBAR1_IN_21: usize = 145;
+ pub const XBAR1_IN_22: usize = 146;
+ pub const XBAR1_IN_23: usize = 147;
+ pub const XBAR1_IN_24: usize = 148;
+ pub const XBAR1_IN_25: usize = 149;
+ pub const XBAR1_IN_26: usize = 150;
+ pub const XBAR1_IN_27: usize = 151;
+ pub const XBAR1_IN_28: usize = 152;
+ pub const XBAR1_IN_29: usize = 153;
+ pub const XBAR1_IN_30: usize = 154;
+ pub const XBAR1_IN_31: usize = 155;
+ pub const XBAR1_IN_32: usize = 156;
+ pub const XBAR1_IN_33: usize = 157;
+ pub const XBAR1_IN_34: usize = 158;
+ pub const XBAR1_IN_35: usize = 159;
+ }
+
+ ral_registers::register! {
+ pub SELECT_INPUT<u32> RW []
+ }
+}
+
+pub mod iomuxc_lpsr {
+ #[repr(C)]
+ #[allow(non_snake_case)]
+ pub struct RegisterBlock {
+ pub SW_MUX_CTL_PAD: [u32; 16],
+ pub SW_PAD_CTL_PAD: [u32; 16],
+ pub SELECT_INPUT: [u32; 24],
+ }
+
+ #[doc(inline)]
+ pub use super::iomuxc::SW_MUX_CTL_PAD::common as SW_MUX_CTL_PAD;
+
+ ral_registers::register! {
+ pub SW_PAD_CTL_PAD<u32> RW [
+ #[doc = "Slew Rate Field"]
+ SRE start(0) width(1) RW {
+ SLOW = 0,
+ FAST = 0x1,
+ }
+ #[doc = "Drive Strength Field"]
+ DSE start(1) width(1) RW {
+ NORMAL = 0,
+ HIGH = 0x1,
+ }
+ #[doc = "Pull / Keep Select Field"]
+ PUE start(2) width(1) RW {}
+ #[doc = "Pull Up / Down Config. Field"]
+ PUS start(3) width(1) RW {
+ PULL_DOWN = 0,
+ PULL_UP = 0x1,
+ }
+ #[doc = "Open Drain LPSR Field"]
+ ODE start(5) width(1) RW {}
+ #[doc = "Domain write protection"]
+ DWP start(28) width(2) RW {}
+ #[doc = "Domain write protection lock"]
+ DWP_LOCK start(30) width(2) RW {}
+ ]
+ }
+
+ pub mod select_input {
+ pub const CAN3_IPP_IND_CANRX: usize = 0;
+ pub const LPI2C5_IPP_IND_LPI2C_SCL: usize = 1;
+ pub const LPI2C5_IPP_IND_LPI2C_SDA: usize = 2;
+ pub const LPI2C6_IPP_IND_LPI2C_SCL: usize = 3;
+ pub const LPI2C6_IPP_IND_LPI2C_SDA: usize = 4;
+ pub const LPSPI5_IPP_IND_LPSPI_PCS_0: usize = 5;
+ pub const LPSPI5_IPP_IND_LPSPI_SCK: usize = 6;
+ pub const LPSPI5_IPP_IND_LPSPI_SDI: usize = 7;
+ pub const LPSPI5_IPP_IND_LPSPI_SDO: usize = 8;
+ pub const LPUART11_IPP_IND_LPUART_RXD: usize = 9;
+ pub const LPUART11_IPP_IND_LPUART_TXD: usize = 10;
+ pub const LPUART12_IPP_IND_LPUART_RXD: usize = 11;
+ pub const LPUART12_IPP_IND_LPUART_TXD: usize = 12;
+ pub const MIC_IPP_IND_MIC_PDM_BITSTREAM_0: usize = 13;
+ pub const MIC_IPP_IND_MIC_PDM_BITSTREAM_1: usize = 14;
+ pub const MIC_IPP_IND_MIC_PDM_BITSTREAM_2: usize = 15;
+ pub const MIC_IPP_IND_MIC_PDM_BITSTREAM_3: usize = 16;
+ pub const NMI_GLUE_IPP_IND_NMI: usize = 17;
+ pub const SAI4_IPG_CLK_SAI_MCLK: usize = 18;
+ pub const SAI4_IPP_IND_SAI_RXBCLK: usize = 19;
+ pub const SAI4_IPP_IND_SAI_RXDATA_0: usize = 20;
+ pub const SAI4_IPP_IND_SAI_RXSYNC: usize = 21;
+ pub const SAI4_IPP_IND_SAI_TXBCLK: usize = 22;
+ pub const SAI4_IPP_IND_SAI_TXSYNC: usize = 23;
+ }
+
+ ral_registers::register! {
+ pub SELECT_INPUT<u32> RW []
+ }
+}
+
+pub mod iomuxc_aon {
+ #[repr(C)]
+ #[allow(non_snake_case)]
+ pub struct RegisterBlock {
+ pub SW_MUX_CTL_PAD: [u32; 29],
+ pub SW_PAD_CTL_PAD: [u32; 29],
+ pub SELECT_INPUT: [u32; 39],
+ }
+
+ #[doc(inline)]
+ pub use super::iomuxc::SW_MUX_CTL_PAD::common as SW_MUX_CTL_PAD;
+
+ ral_registers::register! {
+ pub SW_PAD_CTL_PAD<u32> RW [
+ #[doc = "Slew Rate Field"]
+ SRE start(0) width(1) RW {
+ FAST = 0,
+ SLOW = 0x1,
+ }
+ #[doc = "Drive Strength Field"]
+ DSE start(1) width(1) RW {
+ NORMAL = 0,
+ HIGH = 0x1,
+ }
+ #[doc = "Pull / Keep Select Field"]
+ PUE start(2) width(1) RW {}
+ #[doc = "Pull Up / Down Config. Field"]
+ PUS start(3) width(1) RW {
+ PULL_DOWN = 0,
+ PULL_UP = 0x1,
+ }
+ #[doc = "Open Drain Field"]
+ ODE start(4) width(1) RW {}
+ #[doc = "Domain write protection"]
+ DWP start(24) width(4) RW {}
+ #[doc = "Domain write protection lock"]
+ DWP_LOCK start(28) width(4) RW {}
+ ]
+ }
+
+ pub mod select_input {
+ pub const I3C1_PIN_SCL_IN: usize = 0;
+ pub const I3C1_PIN_SDA_IN: usize = 1;
+ pub const LPI2C1_IPP_IND_LPI2C_SCL: usize = 2;
+ pub const LPI2C1_IPP_IND_LPI2C_SDA: usize = 3;
+ pub const LPI2C2_IPP_IND_LPI2C_SCL: usize = 4;
+ pub const LPI2C2_IPP_IND_LPI2C_SDA: usize = 5;
+ pub const LPSPI1_IPP_IND_LPSPI_PCS_0: usize = 6;
+ pub const LPSPI1_IPP_IND_LPSPI_PCS_1: usize = 7;
+ pub const LPSPI1_IPP_IND_LPSPI_SCK: usize = 8;
+ pub const LPSPI1_IPP_IND_LPSPI_SDI: usize = 9;
+ pub const LPSPI1_IPP_IND_LPSPI_SDO: usize = 10;
+ pub const LPSPI2_IPP_IND_LPSPI_PCS_0: usize = 11;
+ pub const LPSPI2_IPP_IND_LPSPI_PCS_1: usize = 12;
+ pub const LPSPI2_IPP_IND_LPSPI_PCS_3: usize = 13;
+ pub const LPSPI2_IPP_IND_LPSPI_SCK: usize = 14;
+ pub const LPSPI2_IPP_IND_LPSPI_SDI: usize = 15;
+ pub const LPSPI2_IPP_IND_LPSPI_SDO: usize = 16;
+ pub const LPTMR1_IPP_IND_LPTIMER_1: usize = 17;
+ pub const LPTMR1_IPP_IND_LPTIMER_2: usize = 18;
+ pub const LPTMR1_IPP_IND_LPTIMER_3: usize = 19;
+ pub const LPUART1_IPP_IND_LPUART_CTS_N: usize = 20;
+ pub const LPUART1_IPP_IND_LPUART_DCD_N: usize = 21;
+ pub const LPUART1_IPP_IND_LPUART_DSR_N: usize = 22;
+ pub const LPUART12_IPP_IND_LPUART_CTS_N: usize = 23;
+ pub const LPUART12_IPP_IND_LPUART_RXD: usize = 24;
+ pub const LPUART12_IPP_IND_LPUART_TXD: usize = 25;
+ pub const LPUART2_IPP_IND_LPUART_CTS_N: usize = 26;
+ pub const LPUART2_IPP_IND_LPUART_RXD: usize = 27;
+ pub const LPUART2_IPP_IND_LPUART_TXD: usize = 28;
+ pub const LPUART7_IPP_IND_LPUART_CTS_N: usize = 29;
+ pub const LPUART7_IPP_IND_LPUART_RXD: usize = 30;
+ pub const LPUART7_IPP_IND_LPUART_TXD: usize = 31;
+ pub const SAI1_IPG_CLK_SAI_MCLK: usize = 32;
+ pub const SAI1_IPP_IND_SAI_RXBCLK: usize = 33;
+ pub const SAI1_IPP_IND_SAI_RXDATA_0: usize = 34;
+ pub const SAI1_IPP_IND_SAI_RXDATA_1: usize = 35;
+ pub const SAI1_IPP_IND_SAI_RXSYNC: usize = 36;
+ pub const SAI1_IPP_IND_SAI_TXBCLK: usize = 37;
+ pub const SAI1_IPP_IND_SAI_TXSYNC: usize = 38;
+ }
+
+ ral_registers::register! {
+ pub SELECT_INPUT<u32> RW []
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use core::mem::offset_of;
+
+ use super::iomuxc::RegisterBlock as Iomuxc;
+
+ #[test]
+ fn iomuxc_layout() {
+ assert_eq!(offset_of!(Iomuxc, SW_PAD_CTL_PAD.GPIO_AD), 0x350);
+ assert_eq!(offset_of!(Iomuxc, SELECT_INPUT), 0x498);
+ }
+}
diff --git a/drivers/lpspi/Cargo.toml b/drivers/lpspi/Cargo.toml
new file mode 100644
index 0000000..4cbe785
--- /dev/null
+++ b/drivers/lpspi/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "imxrt-drivers-lpspi"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+ral-registers = { workspace = true }
diff --git a/drivers/lpspi/src/lib.rs b/drivers/lpspi/src/lib.rs
new file mode 100644
index 0000000..dcfaf0e
--- /dev/null
+++ b/drivers/lpspi/src/lib.rs
@@ -0,0 +1,877 @@
+#![no_std]
+
+use core::{marker::PhantomData, ops::Range};
+
+pub type LPSPI = ral_registers::Instance<RegisterBlock>;
+
+#[repr(C)]
+#[allow(non_snake_case)]
+pub struct RegisterBlock {
+ pub VERID: u32,
+ pub PARAM: u32,
+ reserved_0: [u8; 8],
+ pub CR: u32,
+ pub SR: u32,
+ pub IER: u32,
+ pub DER: u32,
+ pub CFGR0: u32,
+ pub CFGR1: u32,
+ reserved_1: [u8; 8],
+ pub DMR0: u32,
+ pub DMR1: u32,
+ reserved_2: [u8; 8],
+ pub CCR: u32,
+ reserved_3: [u8; 20],
+ pub FCR: u32,
+ pub FSR: u32,
+ pub TCR: u32,
+ pub TDR: u32,
+ reserved_4: [u8; 8],
+ pub RSR: u32,
+ pub RDR: u32,
+}
+
+ral_registers::register! {
+ #[doc = "Version ID Register"]
+ pub VERID<u32> RO [
+ #[doc = "Module Identification Number"]
+ FEATURE start(0) width(16) RO {
+ #[doc = "Standard feature set supporting a 32-bit shift register."]
+ FEATURE_4 = 0x4,
+ }
+ #[doc = "Minor Version Number"]
+ MINOR start(16) width(8) RO {}
+ #[doc = "Major Version Number"]
+ MAJOR start(24) width(8) RO {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Parameter Register"]
+ pub PARAM<u32> RO [
+ #[doc = "Transmit FIFO Size"]
+ TXFIFO start(0) width(8) RO {}
+ #[doc = "Receive FIFO Size"]
+ RXFIFO start(8) width(8) RO {}
+ #[doc = "PCS Number"]
+ PCSNUM start(16) width(8) RO {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Control Register"]
+ pub CR<u32> RW [
+ #[doc = "Module Enable"]
+ MEN start(0) width(1) RW {}
+ #[doc = "Software Reset"]
+ RST start(1) width(1) RW {}
+ #[doc = "Doze mode enable"]
+ DOZEN start(2) width(1) RW {}
+ #[doc = "Debug Enable"]
+ DBGEN start(3) width(1) RW {}
+ #[doc = "Reset Transmit FIFO"]
+ RTF start(8) width(1) WO {}
+ #[doc = "Reset Receive FIFO"]
+ RRF start(9) width(1) WO {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Status Register"]
+ pub SR<u32> RW [
+ #[doc = "Transmit Data Flag"]
+ TDF start(0) width(1) RO {}
+ #[doc = "Receive Data Flag"]
+ RDF start(1) width(1) RO {}
+ #[doc = "Word Complete Flag"]
+ WCF start(8) width(1) RW {}
+ #[doc = "Frame Complete Flag"]
+ FCF start(9) width(1) RW {}
+ #[doc = "Transfer Complete Flag"]
+ TCF start(10) width(1) RW {}
+ #[doc = "Transmit Error Flag"]
+ TEF start(11) width(1) RW {}
+ #[doc = "Receive Error Flag"]
+ REF start(12) width(1) RW {}
+ #[doc = "Data Match Flag"]
+ DMF start(13) width(1) RW {}
+ #[doc = "Module Busy Flag"]
+ MBF start(24) width(1) RO {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Interrupt Enable Register"]
+ pub IER<u32> RW [
+ #[doc = "Transmit Data Interrupt Enable"]
+ TDIE start(0) width(1) RW {}
+ #[doc = "Receive Data Interrupt Enable"]
+ RDIE start(1) width(1) RW {}
+ #[doc = "Word Complete Interrupt Enable"]
+ WCIE start(8) width(1) RW {}
+ #[doc = "Frame Complete Interrupt Enable"]
+ FCIE start(9) width(1) RW {}
+ #[doc = "Transfer Complete Interrupt Enable"]
+ TCIE start(10) width(1) RW {}
+ #[doc = "Transmit Error Interrupt Enable"]
+ TEIE start(11) width(1) RW {}
+ #[doc = "Receive Error Interrupt Enable"]
+ REIE start(12) width(1) RW {}
+ #[doc = "Data Match Interrupt Enable"]
+ DMIE start(13) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "DMA Enable Register"]
+ pub DER<u32> RW [
+ #[doc = "Transmit Data DMA Enable"]
+ TDDE start(0) width(1) RW {}
+ #[doc = "Receive Data DMA Enable"]
+ RDDE start(1) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Configuration Register 0"]
+ pub CFGR0<u32> RW [
+ #[doc = "Host Request Enable"]
+ HREN start(0) width(1) RW {}
+ #[doc = "Host Request Polarity"]
+ HRPOL start(1) width(1) RW {
+ #[doc = "Active low"]
+ ACTIVE_LOW = 0,
+ #[doc = "Active high"]
+ ACTIVE_HIGH = 0x1,
+ }
+ #[doc = "Host Request Select"]
+ HRSEL start(2) width(1) RW {
+ #[doc = "Host request input is the LPSPI_HREQ pin"]
+ LPSPI_HREQ = 0,
+ #[doc = "Host request input is the input trigger"]
+ INPUT_TRIGGER = 0x1,
+ }
+ #[doc = "Circular FIFO Enable"]
+ CIRFIFO start(8) width(1) RW {}
+ #[doc = "Receive Data Match Only"]
+ RDMO start(9) width(1) RW {
+ #[doc = "Received data is stored in the receive FIFO as in normal operations"]
+ STORE = 0,
+ #[doc = "Received data is discarded unless the Data Match Flag (DMF) is set"]
+ DISCARD = 0x1,
+ }
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Configuration Register 1"]
+ pub CFGR1<u32> RW [
+ #[doc = "Controller Mode"]
+ CONTROLLER start(0) width(1) RW {
+ #[doc = "Peripheral mode"]
+ PERIPHERAL = 0,
+ #[doc = "Controller mode"]
+ CONTROLLER = 0x1,
+ }
+ #[doc = "Sample Point"]
+ SAMPLE start(1) width(1) RW {
+ #[doc = "Input data is sampled on SCK edge"]
+ ON_EDGE = 0,
+ #[doc = "Input data is sampled on delayed SCK edge"]
+ DELAYED_EDGE = 0x1,
+ }
+ #[doc = "Automatic PCS"]
+ AUTOPCS start(2) width(1) RW {}
+ #[doc = "No Stall"]
+ NOSTALL start(3) width(1) RW {
+ #[doc = "Transfers will stall when the transmit FIFO is empty or the receive FIFO is full"]
+ STALL = 0,
+ #[doc = "Transfers will not stall, allowing transmit FIFO underruns or receive FIFO overruns to occur"]
+ NOSTALL = 0x1,
+ }
+ #[doc = "Peripheral Chip Select Polarity"]
+ PCSPOL start(8) width(4) RW {}
+ #[doc = "Match Configuration"]
+ #[doc = ""]
+ #[doc = "`+` is boolean OR. `*` is boolean AND`. The `*_SINGLE` variants never match unless you set the MATCH registers to the same value."]
+ MATCFG start(16) width(3) RW {
+ #[doc = "Match is disabled"]
+ DISABLE = 0,
+ #[doc = "010b - Match is enabled, if 1st data word equals MATCH0 OR MATCH1, i.e., (1st data word = MATCH0 + MATCH1)"]
+ FIRST_WORD_EITHER = 0x2,
+ #[doc = "011b - Match is enabled, if any data word equals MATCH0 OR MATCH1, i.e., (any data word = MATCH0 + MATCH1)"]
+ ANY_WORD_EITHER = 0x3,
+ #[doc = "100b - Match is enabled, if 1st data word equals MATCH0 AND 2nd data word equals MATCH1, i.e., [(1st data word = MATCH0) * (2nd data word = MATCH1)]"]
+ FIRST_WORD_BOTH = 0x4,
+ #[doc = "101b - Match is enabled, if any data word equals MATCH0 AND the next data word equals MATCH1, i.e., [(any data word = MATCH0) * (next data word = MATCH1)]"]
+ ANY_WORD_BOTH = 0x5,
+ #[doc = "110b - Match is enabled, if (1st data word AND MATCH1) equals (MATCH0 AND MATCH1), i.e., [(1st data word * MATCH1) = (MATCH0 * MATCH1)]"]
+ FIRST_WORD_SINGLE = 0x6,
+ #[doc = "111b - Match is enabled, if (any data word AND MATCH1) equals (MATCH0 AND MATCH1), i.e., [(any data word * MATCH1) = (MATCH0 * MATCH1)]"]
+ ANY_WORD_SINGLE = 0x7,
+ }
+ #[doc = "Pin Configuration"]
+ PINCFG start(24) width(2) RW {
+ #[doc = "SIN is used for input data and SOUT is used for output data"]
+ FULL_DUPLEX = 0,
+ #[doc = "SIN is used for both input and output data"]
+ HALF_DUPLEX_SIN = 0x1,
+ #[doc = "SOUT is used for both input and output data"]
+ HALF_DUPLEX_SOUT = 0x2,
+ #[doc = "SOUT is used for input data and SIN is used for output data"]
+ INVERSE = 0x3,
+ }
+ #[doc = "Output Config"]
+ OUTCFG start(26) width(1) RW {
+ #[doc = "Output data retains last value when chip select is negated"]
+ HOLD = 0,
+ #[doc = "Output data is tristated when chip select is negated"]
+ TRISTATE = 0x1,
+ }
+ #[doc = "Peripheral Chip Select Configuration"]
+ PCSCFG start(27) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Data Match Register 0"]
+ pub DMR0<u32> RW []
+}
+
+ral_registers::register! {
+ #[doc = "Data Match Register 1"]
+ pub DMR1<u32> RW []
+}
+
+ral_registers::register! {
+ #[doc = "Clock Configuration Register"]
+ pub CCR<u32> RW [
+ #[doc = "SCK Divider"]
+ SCKDIV start(0) width(8) RW {}
+ #[doc = "Delay Between Transfers"]
+ DBT start(8) width(8) RW {}
+ #[doc = "PCS-to-SCK Delay"]
+ PCSSCK start(16) width(8) RW {}
+ #[doc = "SCK-to-PCS Delay"]
+ SCKPCS start(24) width(8) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "FIFO Control Register"]
+ pub FCR<u32> RW [
+ #[doc = "Transmit FIFO Watermark"]
+ TXWATER start(0) width(4) RW {}
+ #[doc = "Receive FIFO Watermark"]
+ RXWATER start(16) width(4) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "FIFO Status Register"]
+ pub FSR<u32> RO [
+ #[doc = "Transmit FIFO Count"]
+ TXCOUNT start(0) width(5) RO {}
+ #[doc = "Receive FIFO Count"]
+ RXCOUNT start(16) width(5) RO {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Transmit Command Register"]
+ pub TCR<u32> RW [
+ #[doc = "Frame Size"]
+ FRAMESZ start(0) width(12) RW {}
+ #[doc = "Transfer Width"]
+ WIDTH start(16) width(2) RW {
+ #[doc = "1 bit transfer"]
+ SINGLE = 0,
+ #[doc = "2 bit transfer"]
+ DUAL = 0x1,
+ #[doc = "4 bit transfer"]
+ QUAD = 0x2,
+ }
+ #[doc = "Transmit Data Mask"]
+ TXMSK start(18) width(1) RW {}
+ #[doc = "Receive Data Mask"]
+ RXMSK start(19) width(1) RW {}
+ #[doc = "Continuing Command"]
+ CONTC start(20) width(1) RW {}
+ #[doc = "Continuous Transfer"]
+ CONT start(21) width(1) RW {}
+ #[doc = "Byte Swap"]
+ BYSW start(22) width(1) RW {}
+ #[doc = "LSB First"]
+ LSBF start(23) width(1) RW {
+ #[doc = "Data is transferred MSB first"]
+ MSB = 0,
+ #[doc = "Data is transferred LSB first"]
+ LSB = 0x1,
+ }
+ #[doc = "Peripheral Chip Select"]
+ PCS start(24) width(2) RW {
+ #[doc = "Transfer using LPSPI_PCS[0]"]
+ PCS_0 = 0,
+ #[doc = "Transfer using LPSPI_PCS[1]"]
+ PCS_1 = 0x1,
+ #[doc = "Transfer using LPSPI_PCS[2]"]
+ PCS_2 = 0x2,
+ #[doc = "Transfer using LPSPI_PCS[3]"]
+ PCS_3 = 0x3,
+ }
+ #[doc = "Prescaler Value"]
+ PRESCALE start(27) width(3) RW {
+ #[doc = "Divide by 1"]
+ DIVIDE_BY_1= 0,
+ #[doc = "Divide by 2"]
+ DIVIDE_BY_2= 0x1,
+ #[doc = "Divide by 4"]
+ DIVIDE_BY_4= 0x2,
+ #[doc = "Divide by 8"]
+ DIVIDE_BY_8= 0x3,
+ #[doc = "Divide by 16"]
+ DIVIDE_BY_16= 0x4,
+ #[doc = "Divide by 32"]
+ DIVIDE_BY_32= 0x5,
+ #[doc = "Divide by 64"]
+ DIVIDE_BY_64= 0x6,
+ #[doc = "Divide by 128"]
+ DIVIDE_BY_128= 0x7,
+ }
+ #[doc = "Clock Phase"]
+ CPHA start(30) width(1) RW {
+ #[doc = "Data is captured on the leading edge of SCK and changed on the following edge of SCK"]
+ CAPTURE_LEADING_SETUP_FOLLOWING = 0,
+ #[doc = "Data is changed on the leading edge of SCK and captured on the following edge of SCK"]
+ SETUP_LEADING_CAPTURE_FOLLOWING = 0x1,
+ }
+ #[doc = "Clock Polarity"]
+ CPOL start(31) width(1) RW {
+ #[doc = "The inactive state value of SCK is low"]
+ INACTIVE_LOW = 0,
+ #[doc = "The inactive state value of SCK is high"]
+ INACTIVE_HIGH = 0x1,
+ }
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Transmit Data Register"]
+ pub TDR<u32> WO []
+}
+
+ral_registers::register! {
+ #[doc = "Receive Status Register"]
+ pub RSR<u32> RO [
+ #[doc = "Start Of Frame"]
+ SOF start(0) width(1) RO {
+ #[doc = "Subsequent data word received after LPSPI_PCS assertion"]
+ SUBSEQUENT = 0,
+ #[doc = "First data word received after LPSPI_PCS assertion"]
+ FIRST = 0x1,
+ }
+ #[doc = "RX FIFO Empty"]
+ RXEMPTY start(1) width(1) RO {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Receive Data Register"]
+ pub RDR<u32> RO []
+}
+
+/// A command for the next LPSPI transfer.
+#[derive(Debug, Clone, Copy)]
+#[repr(transparent)]
+pub struct TransmitCommand(u32);
+
+impl TransmitCommand {
+ /// Create a transmit command for a single frame.
+ ///
+ /// `frame_size_bits` describes the number of bits to transmit within
+ /// a single transmission. `frame_size_bits` must be at least 8 bits
+ /// large, but no larger than 4096 bits. The device's native word is
+ /// 32 bits large. If there are more than 32 bits to transfer, there
+ /// must be more than one bit remaining.
+ ///
+ /// If the frame size is valid, this returns the command for further
+ /// configuration. Otherwise, this returns `None`.
+ pub const fn for_frame(frame_size_bits: usize) -> Option<Self> {
+ const MIN_FRAME_SIZE: usize = 8;
+ const MAX_FRAME_SIZE: usize = 1 << 12;
+ const WORD_SIZE_BITS: usize = 32;
+
+ if MIN_FRAME_SIZE <= frame_size_bits
+ && frame_size_bits <= MAX_FRAME_SIZE
+ && frame_size_bits % WORD_SIZE_BITS != 1
+ {
+ Some(Self(frame_size_bits as u32 - 1))
+ } else {
+ None
+ }
+ }
+
+ /// Construct a command for the given bytes.
+ pub const fn for_u8s(bytes: &[u8]) -> Option<Self> {
+ Self::for_frame(size_of_val(bytes) * 8)
+ }
+
+ /// Construct a command for the given `u16`s.
+ pub const fn for_u16s(halves: &[u16]) -> Option<Self> {
+ Self::for_frame(size_of_val(halves) * 8)
+ }
+
+ /// Construct a command for the given `u32`s.
+ pub const fn for_u32s(words: &[u32]) -> Option<Self> {
+ Self::for_frame(size_of_val(words) * 8)
+ }
+
+ /// Set the transfer width.
+ ///
+ /// By default, the width is 1 bit.
+ pub const fn with_width(mut self, width: TransferWidth) -> Self {
+ self.0 &= !(0b11 << 16);
+ self.0 |= (width as u32) << 16;
+ self
+ }
+
+ /// Mask or unmask the transmit data.
+ ///
+ /// By default, transmit data is not masked.
+ pub const fn with_transmit_mask(mut self, mask: bool) -> Self {
+ self.0 &= !(1 << 18);
+ self.0 |= (mask as u32) << 18;
+ self
+ }
+
+ /// Mask or unmask the receive data.
+ ///
+ /// By default, receive data is not masked.
+ pub const fn with_receive_mask(mut self, mask: bool) -> Self {
+ self.0 &= !(1 << 19);
+ self.0 |= (mask as u32) << 19;
+ self
+ }
+
+ pub const fn with_continuing(mut self, continuing: bool) -> Self {
+ self.0 &= !(1 << 20);
+ self.0 |= (continuing as u32) << 20;
+ self
+ }
+
+ pub const fn with_continuous(mut self, continuous: bool) -> Self {
+ self.0 &= !(1 << 21);
+ self.0 |= (continuous as u32) << 21;
+ self
+ }
+
+ pub const fn with_byte_swap(mut self, swap: bool) -> Self {
+ self.0 &= !(1 << 22);
+ self.0 |= (swap as u32) << 22;
+ self
+ }
+
+ pub const fn with_lsb_first(mut self, lsb: bool) -> Self {
+ self.0 &= !(1 << 23);
+ self.0 |= (lsb as u32) << 23;
+ self
+ }
+
+ pub const fn with_chip_select(mut self, pcs: Pcs) -> Self {
+ self.0 &= !(0b11 << 24);
+ self.0 |= (pcs as u32) << 24;
+ self
+ }
+
+ pub const fn with_prescale(mut self, prescale: Prescale) -> Self {
+ self.0 &= !(0b111 << 27);
+ self.0 |= (prescale as u32) << 27;
+ self
+ }
+
+ pub const fn with_clock_phase(mut self, phase: ClockPhase) -> Self {
+ self.0 &= !(1 << 30);
+ self.0 |= (phase as u32) << 30;
+ self
+ }
+
+ pub const fn with_clock_polarity(mut self, polarity: ClockPolarity) -> Self {
+ self.0 &= !(1 << 31);
+ self.0 |= (polarity as u32) << 31;
+ self
+ }
+
+ /// Returns the raw command value.
+ pub const fn get(self) -> u32 {
+ self.0
+ }
+}
+
+/// Write the transmit command.
+///
+/// This doesn't check the TX FIFO for space to receive
+/// the command.
+#[inline]
+pub fn write_transmit_command(lpspi: LPSPI, cmd: TransmitCommand) {
+ ral_registers::write_reg!(self, lpspi, TCR, cmd.0);
+}
+
+/// The resting state of clock.
+///
+/// Updates when PCS negates.
+#[derive(Default, Clone, Copy, PartialEq, Eq)]
+#[repr(u32)]
+pub enum ClockPolarity {
+ /// Idle the clock low.
+ #[default]
+ InactiveLow,
+ /// Idle the clock high.
+ InactiveHigh,
+}
+
+/// Describes the clock edge that captures and changes data.
+#[derive(Default, Clone, Copy, PartialEq, Eq)]
+#[repr(u32)]
+pub enum ClockPhase {
+ /// Captured on leading, changed on following.
+ #[default]
+ Captured,
+ /// Changed on leading, captured on following.
+ Changed,
+}
+
+/// Divider applied to all properties in the clock
+/// configuration register.
+#[derive(Default, Clone, Copy, PartialEq, Eq)]
+#[repr(u32)]
+pub enum Prescale {
+ #[default]
+ Div1,
+ Div2,
+ Div4,
+ Div8,
+ Div16,
+ Div32,
+ Div64,
+ Div128,
+}
+
+/// The chip select used for the transfer.
+///
+/// You're responsible for muxing this pin and making
+/// sure it's supported for your peripheral instance.
+#[derive(Default, Clone, Copy, PartialEq, Eq)]
+#[repr(u32)]
+pub enum Pcs {
+ #[default]
+ Pcs0,
+ Pcs1,
+ Pcs2,
+ Pcs3,
+}
+
+/// Describes the data transfer width.
+///
+/// Nominal SPI is [`Bit1`]. Dual and quad SPI can
+/// use additional bits.
+#[derive(Default, Clone, Copy, PartialEq, Eq)]
+#[repr(u32)]
+pub enum TransferWidth {
+ /// One bit for data transfer.
+ #[default]
+ Bit1,
+ /// Two bits for data transfer.
+ Bit2,
+ /// Four bits for data transfer.
+ Bit4,
+}
+
+/// Provides words for LPSPI transmits.
+pub trait TxWordSource {
+ /// Returns how many words are available in this
+ /// source.
+ fn words_available(&self) -> usize;
+
+ /// Produces the next word, or an unspecified
+ /// value if there is no next word.
+ fn next_word(&mut self) -> u32;
+
+ /// Returns `true` if this source can provide
+ /// another word.
+ fn has_word(&self) -> bool {
+ self.words_available() != 0
+ }
+}
+
+/// A destination for LPSPI receives.
+pub trait RxWordSink {
+ /// Returns how many words remain unoccupied
+ /// in this sink.
+ fn words_available(&self) -> usize;
+
+ /// Store the next word, or do nothing if there
+ /// is no more space.
+ fn next_word(&mut self, word: u32);
+
+ /// Returns `true` if this sink can accept another
+ /// word.
+ fn has_word(&self) -> bool {
+ self.words_available() != 0
+ }
+}
+
+/// Produces LPSPI words for transmit.
+///
+/// The LPSPI word size is 32 bits. Stuffing smaller
+/// 8 or 16 bit primitives into that 32 bit word requires
+/// the CPU. This type helps with that stuffing.
+///
+/// Use [`next_word`](Self::next_word) to access the next
+/// available word from your slice. You can write this
+/// word directly to the LPSPI TX FIFO.
+///
+/// To perform a bidirectional I/O with a single buffer,
+/// use [`bidirectional_u8s`], [`bidirectional_u16s`], or
+/// [`bidirectional_u32s`].
+pub struct TxWords<'a, W> {
+ ptr: *const W,
+ end: *const W,
+ _buffer: PhantomData<&'a W>,
+}
+
+impl<'a, W> TxWords<'a, W> {
+ fn new(ptr: *const W, end: *const W) -> Self {
+ Self {
+ ptr,
+ end,
+ _buffer: PhantomData,
+ }
+ }
+
+ fn bytes_available(&self) -> usize {
+ // Safety: pointers remain in range of the same allocation.
+ // Module inspection shows that ptr never crosses beyond
+ // end.
+ unsafe { self.end.byte_offset_from_unsigned(self.ptr) }
+ }
+
+ fn words_available(&self) -> usize {
+ self.bytes_available().div_ceil(size_of::<u32>())
+ }
+
+ fn has_word(&self) -> bool {
+ self.ptr != self.end
+ }
+}
+
+impl<'a> TxWords<'a, u8> {
+ /// Transmit words from a slice of bytes.
+ pub fn from_u8s(bytes: &'a [u8]) -> Self {
+ let Range { start, end } = bytes.as_ptr_range();
+ Self::new(start, end)
+ }
+}
+
+impl TxWordSource for TxWords<'_, u8> {
+ fn words_available(&self) -> usize {
+ TxWords::words_available(self)
+ }
+
+ fn has_word(&self) -> bool {
+ TxWords::has_word(self)
+ }
+
+ fn next_word(&mut self) -> u32 {
+ // Safety: access through pointers guarded by the
+ // number of bytes available in that pointer. Reads
+ // are aligned for a u8 and valid.
+ unsafe {
+ let size = self.bytes_available().min(size_of::<u32>());
+
+ let mut word = 0_u32;
+ if size == 4 {
+ word |= u32::from(self.ptr.add(0).read()) << 24;
+ word |= u32::from(self.ptr.add(1).read()) << 16;
+ word |= u32::from(self.ptr.add(2).read()) << 8;
+ word |= u32::from(self.ptr.add(3).read());
+ } else if size == 3 {
+ word |= u32::from(self.ptr.add(0).read()) << 16;
+ word |= u32::from(self.ptr.add(1).read()) << 8;
+ word |= u32::from(self.ptr.add(2).read());
+ } else if size == 2 {
+ word |= u32::from(self.ptr.add(0).read()) << 8;
+ word |= u32::from(self.ptr.add(1).read());
+ } else if size == 1 {
+ word |= u32::from(self.ptr.read());
+ }
+
+ self.ptr = self.ptr.add(size);
+ word
+ }
+ }
+}
+
+impl<'a> TxWords<'a, u32> {
+ /// Transmit words from a slice of words.
+ pub fn from_u32s(words: &'a [u32]) -> Self {
+ let Range { start, end } = words.as_ptr_range();
+ Self::new(start, end)
+ }
+}
+
+impl TxWordSource for TxWords<'_, u32> {
+ fn words_available(&self) -> usize {
+ TxWords::words_available(self)
+ }
+
+ fn has_word(&self) -> bool {
+ TxWords::has_word(self)
+ }
+
+ fn next_word(&mut self) -> u32 {
+ // Safety: bounds check prevents out of bounds
+ // read. Module inspection shows that ptr never
+ // extends beyond end. Pointer is aligned and
+ // valid for reads given the public API required
+ // to construct this type.
+ unsafe {
+ if self.ptr == self.end {
+ return 0;
+ }
+
+ let word = self.ptr.read();
+ self.ptr = self.ptr.add(1);
+ word
+ }
+ }
+}
+
+/// Place received LPSPI words into a buffer.
+///
+/// The LPSPI word size is 32 bits. Separating that word
+/// into smaller 8 or 16 bit primitives requires the CPU.
+///
+/// After reading the LPSPI RX FIFO, use [`set_next_word`](Self::next_word)
+/// to store it into the user's buffer.
+///
+/// To perform a bidirectional I/O with a single buffer,
+/// use [`bidirectional_u8s`], [`bidirectional_u16s`], or
+/// [`bidirectional_u32s`].
+pub struct RxWords<'a, W> {
+ ptr: *mut W,
+ end: *mut W,
+ _buffer: PhantomData<&'a W>,
+}
+
+impl<'a, W> RxWords<'a, W> {
+ fn new(ptr: *mut W, end: *mut W) -> Self {
+ Self {
+ ptr,
+ end,
+ _buffer: PhantomData,
+ }
+ }
+
+ fn bytes_available(&self) -> usize {
+ // Safety: pointers remain in range of the same allocation.
+ // Module inspection shows that ptr never crosses beyond
+ // end.
+ unsafe { self.end.byte_offset_from_unsigned(self.ptr) }
+ }
+
+ fn words_available(&self) -> usize {
+ self.bytes_available().div_ceil(size_of::<u32>())
+ }
+
+ fn has_word(&self) -> bool {
+ self.ptr != self.end
+ }
+}
+
+impl<'a> RxWords<'a, u8> {
+ /// Read data into a byte slice.
+ pub fn from_u8s(bytes: &'a mut [u8]) -> Self {
+ let Range { start, end } = bytes.as_mut_ptr_range();
+ Self::new(start, end)
+ }
+}
+
+impl RxWordSink for RxWords<'_, u8> {
+ fn words_available(&self) -> usize {
+ RxWords::words_available(self)
+ }
+
+ fn has_word(&self) -> bool {
+ RxWords::has_word(self)
+ }
+
+ fn next_word(&mut self, word: u32) {
+ // Safety: access through pointer guarded by the number
+ // of bytes available through that pointer. Similarly,
+ // pointer offset guaded by the available bytes in the
+ // pointer. Access of u8s, u16s, and u32s are all valid
+ // through u8 access.
+ unsafe {
+ let size = self.bytes_available().min(size_of_val(&word));
+
+ if size == 4 {
+ self.ptr.add(0).write((word >> 24) as u8);
+ self.ptr.add(1).write((word >> 16) as u8);
+ self.ptr.add(2).write((word >> 8) as u8);
+ self.ptr.add(3).write(word as u8);
+ } else if size == 3 {
+ self.ptr.add(0).write((word >> 16) as u8);
+ self.ptr.add(1).write((word >> 8) as u8);
+ self.ptr.add(2).write(word as u8);
+ } else if size == 2 {
+ self.ptr.add(0).write((word >> 8) as u8);
+ self.ptr.add(1).write(word as u8);
+ } else if size == 1 {
+ self.ptr.write(word as u8);
+ }
+
+ self.ptr = self.ptr.add(size);
+ }
+ }
+}
+
+impl<'a> RxWords<'a, u32> {
+ /// Read data into a word slice.
+ pub fn from_u32s(words: &'a mut [u32]) -> Self {
+ let Range { start, end } = words.as_mut_ptr_range();
+ Self::new(start, end)
+ }
+}
+
+impl RxWordSink for RxWords<'_, u32> {
+ fn words_available(&self) -> usize {
+ RxWords::words_available(self)
+ }
+
+ fn has_word(&self) -> bool {
+ RxWords::has_word(self)
+ }
+
+ fn next_word(&mut self, word: u32) {
+ // Safety: pointer remains in bound of allocation,
+ // aligned for u32 access, and valid for writes.
+ unsafe {
+ if self.ptr != self.end {
+ self.ptr.write(word);
+ self.ptr = self.ptr.add(1);
+ }
+ }
+ }
+}
+
+/// Transmit and receive data from this byte slice.
+pub fn bidirectional_u8s<'a>(bytes: &'a mut [u8]) -> (TxWords<'a, u8>, RxWords<'a, u8>) {
+ let Range { start, end } = bytes.as_mut_ptr_range();
+ (TxWords::new(start, end), RxWords::new(start, end))
+}
+
+/// Transmit and receive data from this word slice.
+pub fn bidirectional_u32s<'a>(words: &'a mut [u32]) -> (TxWords<'a, u32>, RxWords<'a, u32>) {
+ let Range { start, end } = words.as_mut_ptr_range();
+ (TxWords::new(start, end), RxWords::new(start, end))
+}
diff --git a/drivers/pit/Cargo.toml b/drivers/pit/Cargo.toml
new file mode 100644
index 0000000..2ddccd0
--- /dev/null
+++ b/drivers/pit/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "imxrt-drivers-pit"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+ral-registers = { workspace = true }
diff --git a/drivers/pit/src/lib.rs b/drivers/pit/src/lib.rs
new file mode 100644
index 0000000..a99d8a2
--- /dev/null
+++ b/drivers/pit/src/lib.rs
@@ -0,0 +1,113 @@
+#![no_std]
+
+use ral_registers as ral;
+pub type PIT = ral_registers::Instance<RegisterBlock>;
+
+#[repr(C)]
+#[allow(non_snake_case)]
+pub struct RegisterBlock {
+ pub MCR: u32,
+ reserved_0: [u8; 220],
+ pub LTMR64H: u32,
+ pub LTMR64L: u32,
+ reserved_1: [u8; 24],
+ pub TIMER: [TIMER::RegisterBlock; 4],
+}
+
+ral_registers::register! {
+ #[doc = "PIT Module Control Register"]
+ pub MCR<u32> RW [
+ #[doc = "Freeze"]
+ FRZ start(0) width(1) RW {
+ #[doc = "Timers continue to run in Debug mode."]
+ RUN = 0,
+ #[doc = "Timers are stopped in Debug mode."]
+ STOP = 0x1,
+ }
+ #[doc = "Module Disable for PIT"]
+ MDIS start(1) width(1) RW {
+ #[doc = "Clock for standard PIT timers is enabled."]
+ ENABLE = 0,
+ #[doc = "Clock for standard PIT timers is disabled."]
+ DISABLE = 0x1,
+ }
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "PIT Upper Lifetime Timer Register"]
+ pub LTMR64H<u32> RO []
+}
+
+ral_registers::register! {
+ #[doc = "PIT Lower Lifetime Timer Register"]
+ pub LTMR64L<u32> RO []
+}
+
+#[allow(non_snake_case)]
+pub mod TIMER {
+
+ #[allow(non_snake_case)]
+ #[repr(C)]
+ pub struct RegisterBlock {
+ pub LDVAL: u32,
+ pub CVAL: u32,
+ pub TCTRL: u32,
+ pub TFLG: u32,
+ }
+
+ ral_registers::register! {
+ #[doc = "Timer Load Value Register"]
+ pub LDVAL<u32> RW []
+ }
+ ral_registers::register! {
+ #[doc = "Timer Current Value Register"]
+ pub CVAL<u32> RO []
+ }
+
+ ral_registers::register! {
+ #[doc = "Timer Control Register"]
+ pub TCTRL<u32> RW [
+ #[doc = "Timer Enable"]
+ TEN start(0) width(1) RW {}
+ #[doc = "Timer Interrupt Enable"]
+ TIE start(1) width(1) RW {}
+ #[doc = "Chain Mode"]
+ CHN start(2) width(1) RW {}
+ ]
+ }
+
+ ral_registers::register! {
+ #[doc = "Timer Flag Register"]
+ pub TFLG<u32> RW [
+ #[doc = "Timer Interrupt Flag"]
+ TIF start(0) width(1) RW {
+ }
+ ]
+ }
+}
+
+/// Read the lifetime counter value as a `u64`.
+///
+/// This routine assumes that you've chained timers 0 and
+/// timers 1 together, forming the lifetime timer. It also
+/// assumes that the timers are enabled.
+///
+/// The implementation includes the recommendation in errata
+/// ERR050130.
+pub fn read_lifetime_value(pit: PIT) -> u64 {
+ let mut h = ral::read_reg!(self, pit, LTMR64H);
+ let mut l = ral::read_reg!(self, pit, LTMR64L);
+
+ // ERR050130 says
+ //
+ // "[...] if the read value of LTMR64L is equal to LDVAL0 [...]"
+ if ral::read_reg!(self, pit, TIMER[0].LDVAL) == l {
+ // "[...] then read both LTMR64H and LTMR64L registers for
+ // one additional time to obtain the correct lifetime value."
+ h = ral::read_reg!(self, pit, LTMR64H);
+ l = ral::read_reg!(self, pit, LTMR64L);
+ }
+
+ (u64::from(h) << 32) | u64::from(l)
+}
diff --git a/drivers/pmu-11xx/Cargo.toml b/drivers/pmu-11xx/Cargo.toml
new file mode 100644
index 0000000..df193d2
--- /dev/null
+++ b/drivers/pmu-11xx/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "imxrt-drivers-pmu-11xx"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+ral-registers = { workspace = true }
diff --git a/drivers/pmu-11xx/src/lib.rs b/drivers/pmu-11xx/src/lib.rs
new file mode 100644
index 0000000..2adec1f
--- /dev/null
+++ b/drivers/pmu-11xx/src/lib.rs
@@ -0,0 +1,139 @@
+#![no_std]
+
+pub type Instance = ral_registers::Instance<RegisterBlock>;
+
+#[repr(C)]
+#[allow(non_snake_case)]
+pub struct RegisterBlock {
+ reserved_0: [u8; 1280],
+ pub PMU_LDO_PLL: u32,
+ reserved_1: [u8; 76],
+ pub PMU_BIAS_CTRL: u32,
+ reserved_2: [u8; 12],
+ pub PMU_BIAS_CTRL2: u32,
+ reserved_3: [u8; 12],
+ pub PMU_REF_CTRL: u32,
+ reserved_4: [u8; 12],
+ pub PMU_POWER_DETECT_CTRL: u32,
+ reserved_5: [u8; 124],
+ pub LDO_PLL_ENABLE_SP: u32,
+ reserved_6: [u8; 12],
+ pub LDO_LPSR_ANA_ENABLE_SP: u32,
+ reserved_7: [u8; 12],
+ pub LDO_LPSR_ANA_LP_MODE_SP: u32,
+ reserved_8: [u8; 12],
+ pub LDO_LPSR_ANA_TRACKING_EN_SP: u32,
+ reserved_9: [u8; 12],
+ pub LDO_LPSR_ANA_BYPASS_EN_SP: u32,
+ reserved_10: [u8; 12],
+ pub LDO_LPSR_ANA_STBY_EN_SP: u32,
+ reserved_11: [u8; 12],
+ pub LDO_LPSR_DIG_ENABLE_SP: u32,
+ reserved_12: [u8; 12],
+ pub LDO_LPSR_DIG_TRG_SP0: u32,
+ reserved_13: [u8; 12],
+ pub LDO_LPSR_DIG_TRG_SP1: u32,
+ reserved_14: [u8; 12],
+ pub LDO_LPSR_DIG_TRG_SP2: u32,
+ reserved_15: [u8; 12],
+ pub LDO_LPSR_DIG_TRG_SP3: u32,
+ reserved_16: [u8; 12],
+ pub LDO_LPSR_DIG_LP_MODE_SP: u32,
+ reserved_17: [u8; 12],
+ pub LDO_LPSR_DIG_TRACKING_EN_SP: u32,
+ reserved_18: [u8; 12],
+ pub LDO_LPSR_DIG_BYPASS_EN_SP: u32,
+ reserved_19: [u8; 12],
+ pub LDO_LPSR_DIG_STBY_EN_SP: u32,
+ reserved_20: [u8; 12],
+ pub BANDGAP_ENABLE_SP: u32,
+ reserved_21: [u8; 28],
+ pub RBB_SOC_ENABLE_SP: u32,
+ reserved_22: [u8; 12],
+ pub RBB_LPSR_ENABLE_SP: u32,
+ reserved_23: [u8; 12],
+ pub BANDGAP_STBY_EN_SP: u32,
+ reserved_24: [u8; 12],
+ pub PLL_LDO_STBY_EN_SP: u32,
+ reserved_25: [u8; 28],
+ pub RBB_SOC_STBY_EN_SP: u32,
+ reserved_26: [u8; 12],
+ pub RBB_LPSR_STBY_EN_SP: u32,
+ reserved_27: [u8; 28],
+ pub RBB_LPSR_CONFIGURE: u32,
+ reserved_28: [u8; 12],
+ pub RBB_SOC_CONFIGURE: u32,
+ reserved_29: [u8; 12],
+ pub REFTOP_OTP_TRIM_VALUE: u32,
+ reserved_30: [u8; 28],
+ pub LPSR_1P8_LDO_OTP_TRIM_VALUE: u32,
+}
+
+ral_registers::register! {
+ #[doc = "PMU_LDO_PLL_REGISTER"]
+ pub PMU_LDO_PLL<u32> RW [
+ #[doc = "LDO_PLL_ENABLE"]
+ LDO_PLL_ENABLE start(0) width(1) RW {}
+ #[doc = "LDO_PLL_CONTROL_MODE"]
+ LDO_PLL_CONTROL_MODE start(1) width(1) RW {
+ #[doc = "SW Control"]
+ SOFTWARE = 0,
+ #[doc = "HW Control"]
+ HARDWARE = 0x1,
+ }
+ #[doc = "ldo_pll_ai_toggle"]
+ LDO_PLL_AI_TOGGLE start(16) width(1) RW {}
+ #[doc = "ldo_pll_busy"]
+ LDO_PLL_AI_BUSY start(30) width(1) RO {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "LDO_PLL_ENABLE_SP_REGISTER"]
+ pub LDO_PLL_ENABLE_SP<u32> RW []
+}
+
+ral_registers::register! {
+ #[doc = "PMU_REF_CTRL_REGISTER"]
+ pub PMU_REF_CTRL<u32> RW [
+ #[doc = "ref_ai_toggle"]
+ REF_AI_TOGGLE start(0) width(1) RW {}
+ #[doc = "ref_ai_busy"]
+ REF_AI_BUSY start(1) width(1) RO {}
+ #[doc = "REF_ENABLE"]
+ REF_ENABLE start(2) width(1) RW {}
+ #[doc = "REF_CONTROL_MODE"]
+ REF_CONTROL_MODE start(3) width(1) RW {
+ #[doc = "SW Control"]
+ SOFTWRE = 0,
+ #[doc = "HW Control"]
+ HARDWARE = 0x1,
+ }
+ #[doc = "en_pll_vol_ref_buffer"]
+ EN_PLL_VOL_REF_BUFFER start(4) width(1) RW {}
+ ]
+}
+
+/// Specify which setpoints enable the PHY LDO.
+///
+/// A high bit in `setpoints` signals "enable."
+pub fn set_phy_ldo_setpoints(pmu: Instance, setpoints: u16) {
+ // Logic is inverted in hardware.
+ let setpoints: u16 = !setpoints;
+ ral_registers::write_reg!(self, pmu, LDO_PLL_ENABLE_SP, setpoints as u32);
+}
+
+/// Manage the PHY LDO with setpoints.
+pub fn enable_phy_ldo_setpoints(pmu: Instance) {
+ ral_registers::modify_reg!(self, pmu, PMU_LDO_PLL, LDO_PLL_CONTROL_MODE: HARDWARE);
+}
+
+/// Manage the PLL reference with setpoints.
+pub fn enable_pll_reference_setpoints(pmu: Instance) {
+ ral_registers::modify_reg!(self, pmu, PMU_REF_CTRL, REF_CONTROL_MODE: HARDWARE);
+}
+
+/// Enable or disable the PLL bandgap reference voltage.
+pub fn enable_pll_reference_voltage(pmu: Instance, enable: bool) {
+ ral_registers::modify_reg!(self, pmu, PMU_REF_CTRL, EN_PLL_VOL_REF_BUFFER: enable as u32);
+}
diff --git a/drivers/rgpio/Cargo.toml b/drivers/rgpio/Cargo.toml
new file mode 100644
index 0000000..c4702c5
--- /dev/null
+++ b/drivers/rgpio/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "imxrt-drivers-rgpio"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+ral-registers = { workspace = true }
diff --git a/drivers/rgpio/src/lib.rs b/drivers/rgpio/src/lib.rs
new file mode 100644
index 0000000..c190e48
--- /dev/null
+++ b/drivers/rgpio/src/lib.rs
@@ -0,0 +1,164 @@
+#![no_std]
+
+#[repr(C)]
+#[allow(non_snake_case)]
+pub struct RegisterBlock {
+ pub VERID: u32,
+ pub PARAM: u32,
+ _reserved_0: [u8; 4],
+ pub LOCK: u32,
+ pub PCNS: u32,
+ pub ICNS: u32,
+ pub PCNP: u32,
+ pub ICNP: u32,
+ _reserved_1: [u8; 32],
+ pub PDOR: u32,
+ pub PSOR: u32,
+ pub PCOR: u32,
+ pub PTOR: u32,
+ pub PDIR: u32,
+ pub PDDR: u32,
+ pub PIDR: u32,
+ _reserved_2: [u8; 4],
+ pub PDR: [u8; 32],
+ pub ICR: [u32; 32],
+ pub GICR: [u32; 2],
+ _reserved_3: [u8; 24],
+ pub ISFR: [u32; 2],
+}
+
+ral_registers::register! {
+ #[doc = "Port Data Output Register"]
+ pub PDOR<u32> RW []
+}
+
+ral_registers::register! {
+ #[doc = "Port Set Output Register"]
+ pub PSOR<u32> WO []
+}
+
+ral_registers::register! {
+ #[doc = "Port Clear Output Register"]
+ pub PCOR<u32> WO []
+}
+
+ral_registers::register! {
+ #[doc = "Port Toggle Output Register"]
+ pub PTOR<u32> WO []
+}
+
+ral_registers::register! {
+ #[doc = "Port Data Input Register"]
+ pub PDIR<u32> RO []
+}
+
+ral_registers::register! {
+ #[doc = "Port Data Direction Register"]
+ pub PDDR<u32> RW []
+}
+
+ral_registers::register! {
+ #[doc = "Port Input Disable Register"]
+ pub PIDR<u32> RW []
+}
+
+ral_registers::register! {
+ #[doc = "Port Data Register"]
+ pub PDR<u8> RW []
+}
+
+ral_registers::register! {
+ #[doc = "Interrupt Control Register"]
+ pub ICR<u32> RW [
+ #[doc = "Interrupt Configuration"]
+ IRQC start(16) width(4) RW {
+ #[doc = "Interrupt Status Flag (ISF) is disabled."]
+ DISABLED = 0,
+ #[doc = "ISF flag and DMA request on rising edge."]
+ ISF_DMA_RISING = 0x1,
+ #[doc = "ISF flag and DMA request on falling edge."]
+ ISF_DMA_FALLING = 0x2,
+ #[doc = "ISF flag and DMA request on either edge."]
+ ISF_DMA_EITHER = 0x3,
+ #[doc = "ISF flag sets on rising edge."]
+ ISF_RISING = 0x5,
+ #[doc = "ISF flag sets on falling edge."]
+ ISF_FALLING = 0x6,
+ #[doc = "ISF flag sets on either edge."]
+ ISF_EITHER = 0x7,
+ #[doc = "ISF flag and Interrupt when logic 0."]
+ ISF_INTERRUPT_LOW = 0x8,
+ #[doc = "ISF flag and Interrupt on rising-edge."]
+ ISF_INTERRUPT_RISING = 0x9,
+ #[doc = "ISF flag and Interrupt on falling-edge."]
+ ISF_INTERRUPT_FALLING = 0xA,
+ #[doc = "ISF flag and Interrupt on either edge."]
+ ISF_INTERRUPT_EITHER = 0xB,
+ #[doc = "ISF flag and Interrupt when logic 1."]
+ ISF_INTERRUPT_HIGH = 0xC,
+ }
+ #[doc = "Interrupt Select"]
+ IRQS start(20) width(1) RW {}
+ #[doc = "Lock Register"]
+ LK start(23) width(1) RW {}
+ #[doc = "Interrupt Status Flag"]
+ ISF start(24) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Global Interrupt Control Register"]
+ pub GICR<u32> WO [
+ #[doc = "Global Interrupt Write Enable"]
+ GIWE start(0) width(16) WO {}
+ #[doc = "Interrupt Configuration"]
+ IRQC start(16) width(4) WO {
+ #[doc = "Interrupt Status Flag (ISF) is disabled."]
+ DISABLED = 0,
+ #[doc = "ISF flag and DMA request on rising edge."]
+ ISF_DMA_RISING = 0x1,
+ #[doc = "ISF flag and DMA request on falling edge."]
+ ISF_DMA_FALLING = 0x2,
+ #[doc = "ISF flag and DMA request on either edge."]
+ ISF_DMA_EITHER = 0x3,
+ #[doc = "ISF flag sets on rising edge."]
+ ISF_RISING = 0x5,
+ #[doc = "ISF flag sets on falling edge."]
+ ISF_FALLING = 0x6,
+ #[doc = "ISF flag sets on either edge."]
+ ISF_EITHER = 0x7,
+ #[doc = "ISF flag and Interrupt when logic 0."]
+ ISF_INTERRUPT_LOW = 0x8,
+ #[doc = "ISF flag and Interrupt on rising-edge."]
+ ISF_INTERRUPT_RISING = 0x9,
+ #[doc = "ISF flag and Interrupt on falling-edge."]
+ ISF_INTERRUPT_FALLING = 0xA,
+ #[doc = "ISF flag and Interrupt on either edge."]
+ ISF_INTERRUPT_EITHER = 0xB,
+ #[doc = "ISF flag and Interrupt when logic 1."]
+ ISF_INTERRUPT_HIGH = 0xC,
+ }
+ #[doc = "Interrupt Select"]
+ IRQS start(20) width(1) WO {}
+ #[doc = "Lock Register"]
+ LK start(23) width(1) WO {}
+ #[doc = "Interrupt Status Flag"]
+ ISF start(24) width(1) WO {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Interrupt Status Flag Register"]
+ pub ISFR<u32> RW []
+}
+
+#[cfg(test)]
+mod tests {
+ use super::RegisterBlock;
+ use core::mem::offset_of;
+
+ #[test]
+ fn layout() {
+ assert_eq!(offset_of!(RegisterBlock, ISFR), 0x120);
+ }
+}
diff --git a/drivers/rtwdog/Cargo.toml b/drivers/rtwdog/Cargo.toml
new file mode 100644
index 0000000..b40e3d6
--- /dev/null
+++ b/drivers/rtwdog/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "imxrt-drivers-rtwdog"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+ral-registers = { workspace = true }
diff --git a/drivers/rtwdog/src/lib.rs b/drivers/rtwdog/src/lib.rs
new file mode 100644
index 0000000..9734813
--- /dev/null
+++ b/drivers/rtwdog/src/lib.rs
@@ -0,0 +1,95 @@
+#![no_std]
+
+pub type Instance = ral_registers::Instance<RegisterBlock>;
+
+#[allow(non_snake_case)]
+#[repr(C)]
+pub struct RegisterBlock {
+ pub CS: u32,
+ pub CNT: u32,
+ pub TOVAL: u32,
+ pub WIN: u32,
+}
+
+/// Written to `CNT` during the unlock sequence.
+pub const RTWDOG_MAGIC: u32 = 0xD928_C520;
+
+ral_registers::register! {
+ #[doc = "Watchdog Control and Status Register"]
+ pub CS<u32> RW [
+ #[doc = "Stop Enable"]
+ STOP start(0) width(1) RW {}
+ #[doc = "Wait Enable"]
+ WAIT start(1) width(1) RW {}
+ #[doc = "Debug Enable"]
+ DBG start(2) width(1) RW {}
+ #[doc = "Watchdog Test"]
+ TST start(3) width(2) RW {
+ #[doc = "Watchdog test mode disabled."]
+ DISABLED = 0,
+ #[doc = "Watchdog user mode enabled. (Watchdog test mode disabled.) After testing the watchdog, software should use this setting to indicate that the watchdog is functioning normally in user mode."]
+ USER_MODE = 0x1,
+ #[doc = "Watchdog test mode enabled, only the low byte is used. CNT[CNTLOW] is compared with TOVAL[TOVALLOW]."]
+ TEST_LOW_BYTE_ONLY = 0x2,
+ #[doc = "Watchdog test mode enabled, only the high byte is used. CNT[CNTHIGH] is compared with TOVAL[TOVALHIGH]."]
+ TEST_HIGH_BYTE_ONLY = 0x3,
+ }
+ #[doc = "Allow updates"]
+ UPDATE start(5) width(1) RW {}
+ #[doc = "Watchdog Interrupt"]
+ INT start(6) width(1) RW {}
+ #[doc = "Watchdog Enable"]
+ EN start(7) width(1) RW {}
+ #[doc = "Watchdog Clock"]
+ CLK start(8) width(2) RW {}
+ #[doc = "Reconfiguration Success"]
+ RCS start(10) width(1) RO {}
+ #[doc = "Unlock status"]
+ ULK start(11) width(1) RO {}
+ #[doc = "Watchdog prescaler"]
+ PRES start(12) width(1) RW {}
+ #[doc = "Enables or disables WDOG support for 32-bit (otherwise 16-bit or 8-bit) refresh/unlock command write words"]
+ CMD32EN start(13) width(1) RW {}
+ #[doc = "Watchdog Interrupt Flag"]
+ FLG start(14) width(1) RW {}
+ #[doc = "Watchdog Window"]
+ WIN start(15) width(1) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Watchdog Counter Register"]
+ pub CNT<u32> RW [
+ #[doc = "Low byte of the Watchdog Counter"]
+ CNTLOW start(0) width(8) RW {}
+ #[doc = "High byte of the Watchdog Counter"]
+ CNTHIGH start(8) width(8) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Watchdog Timeout Value Register"]
+ pub TOVAL<u32> RW [
+ #[doc = "Low byte of the timeout value"]
+ TOVALLOW start(0) width(8) RW {}
+ #[doc = "High byte of the timeout value"]
+ TOVALHIGH start(8) width(8) RW {}
+ ]
+}
+
+ral_registers::register! {
+ #[doc = "Watchdog Window Register"]
+ pub WIN<u32> RW [
+ #[doc = "Low byte of Watchdog Window"]
+ WINLOW start(0) width(8) RW {}
+ #[doc = "High byte of Watchdog Window"]
+ WINHIGH start(8) width(8) RW {}
+ ]
+}
+
+/// Unlike and disable the RTWDOG.
+#[inline]
+pub fn disable(rtwdog: Instance) {
+ ral_registers::write_reg!(self, rtwdog, CNT, RTWDOG_MAGIC);
+ ral_registers::modify_reg!(self, rtwdog, CS, EN: 0);
+}