- > {
+ unsafe { slice.get_unchecked_mut().iter_mut().map(|v| Pin::new_unchecked(v)) }
}
+ for (i, fut) in pin_iter(self.inner.as_mut()).enumerate() {
+ if let Poll::Ready(res) = fut.poll(cx) {
+ return Poll::Ready((res, i));
+ }
+ }
+
+ Poll::Pending
}
}
diff --git a/embassy-hal-internal/Cargo.toml b/embassy-hal-internal/Cargo.toml
index d5ca95ac2..cc360682e 100644
--- a/embassy-hal-internal/Cargo.toml
+++ b/embassy-hal-internal/Cargo.toml
@@ -28,7 +28,7 @@ prio-bits-8 = []
cortex-m = ["dep:cortex-m", "dep:critical-section"]
[dependencies]
-defmt = { version = "0.3", optional = true }
+defmt = { version = "1.0.1", optional = true }
log = { version = "0.4.14", optional = true }
num-traits = { version = "0.2.14", default-features = false }
diff --git a/embassy-hal-internal/src/lib.rs b/embassy-hal-internal/src/lib.rs
index 89f20e993..7addb71e2 100644
--- a/embassy-hal-internal/src/lib.rs
+++ b/embassy-hal-internal/src/lib.rs
@@ -11,7 +11,7 @@ pub mod drop;
mod macros;
mod peripheral;
pub mod ratio;
-pub use peripheral::{Peripheral, PeripheralRef};
+pub use peripheral::{Peri, PeripheralType};
#[cfg(feature = "cortex-m")]
pub mod interrupt;
diff --git a/embassy-hal-internal/src/macros.rs b/embassy-hal-internal/src/macros.rs
index 07cd89487..ce72ded5c 100644
--- a/embassy-hal-internal/src/macros.rs
+++ b/embassy-hal-internal/src/macros.rs
@@ -8,6 +8,8 @@ macro_rules! peripherals_definition {
$(#[$cfg])?
#[allow(non_camel_case_types)]
#[doc = concat!(stringify!($name), " peripheral")]
+ #[derive(Debug)]
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct $name { _private: () }
$(#[$cfg])?
@@ -18,8 +20,8 @@ macro_rules! peripherals_definition {
///
/// You must ensure that you're only using one instance of this type at a time.
#[inline]
- pub unsafe fn steal() -> Self {
- Self{ _private: ()}
+ pub unsafe fn steal() -> $crate::Peri<'static, Self> {
+ $crate::Peri::new_unchecked(Self{ _private: ()})
}
}
@@ -42,7 +44,7 @@ macro_rules! peripherals_struct {
$(
#[doc = concat!(stringify!($name), " peripheral")]
$(#[$cfg])?
- pub $name: peripherals::$name,
+ pub $name: $crate::Peri<'static, peripherals::$name>,
)*
}
@@ -108,28 +110,26 @@ macro_rules! peripherals {
};
}
-/// Convenience converting into reference.
-#[macro_export]
-macro_rules! into_ref {
- ($($name:ident),*) => {
- $(
- let mut $name = $name.into_ref();
- )*
- }
-}
-
/// Implement the peripheral trait.
#[macro_export]
macro_rules! impl_peripheral {
- ($type:ident) => {
- impl $crate::Peripheral for $type {
- type P = $type;
-
- #[inline]
- unsafe fn clone_unchecked(&self) -> Self::P {
- #[allow(clippy::needless_update)]
- $type { ..*self }
+ ($type:ident<$($T:ident $(: $bound:tt $(+ $others:tt )*)?),*>) => {
+ impl<$($T: $($bound $(+$others)*)?),*> Copy for $type <$($T),*> {}
+ impl<$($T: $($bound $(+$others)*)?),*> Clone for $type <$($T),*> {
+ fn clone(&self) -> Self {
+ *self
}
}
+ impl<$($T: $($bound $(+$others)*)?),*> PeripheralType for $type <$($T),*> {}
+ };
+
+ ($type:ident) => {
+ impl Copy for $type {}
+ impl Clone for $type {
+ fn clone(&self) -> Self {
+ *self
+ }
+ }
+ impl $crate::PeripheralType for $type {}
};
}
diff --git a/embassy-hal-internal/src/peripheral.rs b/embassy-hal-internal/src/peripheral.rs
index 0b0f13338..b1868caf6 100644
--- a/embassy-hal-internal/src/peripheral.rs
+++ b/embassy-hal-internal/src/peripheral.rs
@@ -1,5 +1,5 @@
use core::marker::PhantomData;
-use core::ops::{Deref, DerefMut};
+use core::ops::Deref;
/// An exclusive reference to a peripheral.
///
@@ -9,20 +9,28 @@ use core::ops::{Deref, DerefMut};
/// - Memory efficiency: Peripheral singletons are typically either zero-sized (for concrete
/// peripherals like `PA9` or `SPI4`) or very small (for example `AnyPin`, which is 1 byte).
/// However `&mut T` is always 4 bytes for 32-bit targets, even if T is zero-sized.
-/// PeripheralRef stores a copy of `T` instead, so it's the same size.
+/// Peripheral stores a copy of `T` instead, so it's the same size.
/// - Code size efficiency. If the user uses the same driver with both `SPI4` and `&mut SPI4`,
-/// the driver code would be monomorphized two times. With PeripheralRef, the driver is generic
-/// over a lifetime only. `SPI4` becomes `PeripheralRef<'static, SPI4>`, and `&mut SPI4` becomes
-/// `PeripheralRef<'a, SPI4>`. Lifetimes don't cause monomorphization.
-pub struct PeripheralRef<'a, T> {
+/// the driver code would be monomorphized two times. With Peri, the driver is generic
+/// over a lifetime only. `SPI4` becomes `Peri<'static, SPI4>`, and `&mut SPI4` becomes
+/// `Peri<'a, SPI4>`. Lifetimes don't cause monomorphization.
+#[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub struct Peri<'a, T: PeripheralType> {
inner: T,
_lifetime: PhantomData<&'a mut T>,
}
-impl<'a, T> PeripheralRef<'a, T> {
- /// Create a new reference to a peripheral.
+impl<'a, T: PeripheralType> Peri<'a, T> {
+ /// Create a new owned a peripheral.
+ ///
+ /// For use by HALs only.
+ ///
+ /// If you're an end user you shouldn't use this, you should use `steal()`
+ /// on the actual peripheral types instead.
#[inline]
- pub fn new(inner: T) -> Self {
+ #[doc(hidden)]
+ pub unsafe fn new_unchecked(inner: T) -> Self {
Self {
inner,
_lifetime: PhantomData,
@@ -38,46 +46,38 @@ impl<'a, T> PeripheralRef<'a, T> {
/// create two SPI drivers on `SPI1`, because they will "fight" each other.
///
/// You should strongly prefer using `reborrow()` instead. It returns a
- /// `PeripheralRef` that borrows `self`, which allows the borrow checker
+ /// `Peri` that borrows `self`, which allows the borrow checker
/// to enforce this at compile time.
- pub unsafe fn clone_unchecked(&self) -> PeripheralRef<'a, T>
- where
- T: Peripheral
,
- {
- PeripheralRef::new(self.inner.clone_unchecked())
+ pub unsafe fn clone_unchecked(&self) -> Peri<'a, T> {
+ Peri::new_unchecked(self.inner)
}
- /// Reborrow into a "child" PeripheralRef.
+ /// Reborrow into a "child" Peri.
///
- /// `self` will stay borrowed until the child PeripheralRef is dropped.
- pub fn reborrow(&mut self) -> PeripheralRef<'_, T>
- where
- T: Peripheral
,
- {
- // safety: we're returning the clone inside a new PeripheralRef that borrows
+ /// `self` will stay borrowed until the child Peripheral is dropped.
+ pub fn reborrow(&mut self) -> Peri<'_, T> {
+ // safety: we're returning the clone inside a new Peripheral that borrows
// self, so user code can't use both at the same time.
- PeripheralRef::new(unsafe { self.inner.clone_unchecked() })
+ unsafe { self.clone_unchecked() }
}
/// Map the inner peripheral using `Into`.
///
- /// This converts from `PeripheralRef<'a, T>` to `PeripheralRef<'a, U>`, using an
+ /// This converts from `Peri<'a, T>` to `Peri<'a, U>`, using an
/// `Into` impl to convert from `T` to `U`.
///
- /// For example, this can be useful to degrade GPIO pins: converting from PeripheralRef<'a, PB11>` to `PeripheralRef<'a, AnyPin>`.
+ /// For example, this can be useful to.into() GPIO pins: converting from Peri<'a, PB11>` to `Peri<'a, AnyPin>`.
#[inline]
- pub fn map_into(self) -> PeripheralRef<'a, U>
+ pub fn into(self) -> Peri<'a, U>
where
T: Into,
+ U: PeripheralType,
{
- PeripheralRef {
- inner: self.inner.into(),
- _lifetime: PhantomData,
- }
+ unsafe { Peri::new_unchecked(self.inner.into()) }
}
}
-impl<'a, T> Deref for PeripheralRef<'a, T> {
+impl<'a, T: PeripheralType> Deref for Peri<'a, T> {
type Target = T;
#[inline]
@@ -86,92 +86,5 @@ impl<'a, T> Deref for PeripheralRef<'a, T> {
}
}
-/// Trait for any type that can be used as a peripheral of type `P`.
-///
-/// This is used in driver constructors, to allow passing either owned peripherals (e.g. `TWISPI0`),
-/// or borrowed peripherals (e.g. `&mut TWISPI0`).
-///
-/// For example, if you have a driver with a constructor like this:
-///
-/// ```ignore
-/// impl<'d, T: Instance> Twim<'d, T> {
-/// pub fn new(
-/// twim: impl Peripheral
+ 'd,
-/// irq: impl Peripheral
+ 'd,
-/// sda: impl Peripheral
+ 'd,
-/// scl: impl Peripheral
+ 'd,
-/// config: Config,
-/// ) -> Self { .. }
-/// }
-/// ```
-///
-/// You may call it with owned peripherals, which yields an instance that can live forever (`'static`):
-///
-/// ```ignore
-/// let mut twi: Twim<'static, ...> = Twim::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config);
-/// ```
-///
-/// Or you may call it with borrowed peripherals, which yields an instance that can only live for as long
-/// as the borrows last:
-///
-/// ```ignore
-/// let mut twi: Twim<'_, ...> = Twim::new(&mut p.TWISPI0, &mut irq, &mut p.P0_03, &mut p.P0_04, config);
-/// ```
-///
-/// # Implementation details, for HAL authors
-///
-/// When writing a HAL, the intended way to use this trait is to take `impl Peripheral
` in
-/// the HAL's public API (such as driver constructors), calling `.into_ref()` to obtain a `PeripheralRef`,
-/// and storing that in the driver struct.
-///
-/// `.into_ref()` on an owned `T` yields a `PeripheralRef<'static, T>`.
-/// `.into_ref()` on an `&'a mut T` yields a `PeripheralRef<'a, T>`.
-pub trait Peripheral: Sized {
- /// Peripheral singleton type
- type P;
-
- /// Unsafely clone (duplicate) a peripheral singleton.
- ///
- /// # Safety
- ///
- /// This returns an owned clone of the peripheral. You must manually ensure
- /// only one copy of the peripheral is in use at a time. For example, don't
- /// create two SPI drivers on `SPI1`, because they will "fight" each other.
- ///
- /// You should strongly prefer using `into_ref()` instead. It returns a
- /// `PeripheralRef`, which allows the borrow checker to enforce this at compile time.
- unsafe fn clone_unchecked(&self) -> Self::P;
-
- /// Convert a value into a `PeripheralRef`.
- ///
- /// When called on an owned `T`, yields a `PeripheralRef<'static, T>`.
- /// When called on an `&'a mut T`, yields a `PeripheralRef<'a, T>`.
- #[inline]
- fn into_ref<'a>(self) -> PeripheralRef<'a, Self::P>
- where
- Self: 'a,
- {
- PeripheralRef::new(unsafe { self.clone_unchecked() })
- }
-}
-
-impl<'b, T: DerefMut> Peripheral for T
-where
- T::Target: Peripheral,
-{
- type P = ::P;
-
- #[inline]
- unsafe fn clone_unchecked(&self) -> Self::P {
- T::Target::clone_unchecked(self)
- }
-}
-
-impl<'b, T: Peripheral> Peripheral for PeripheralRef<'_, T> {
- type P = T::P;
-
- #[inline]
- unsafe fn clone_unchecked(&self) -> Self::P {
- T::clone_unchecked(self)
- }
-}
+/// Marker trait for peripheral types.
+pub trait PeripheralType: Copy + Sized {}
diff --git a/embassy-imxrt/Cargo.toml b/embassy-imxrt/Cargo.toml
new file mode 100644
index 000000000..49dc8089c
--- /dev/null
+++ b/embassy-imxrt/Cargo.toml
@@ -0,0 +1,100 @@
+[package]
+name = "embassy-imxrt"
+version = "0.1.0"
+edition = "2021"
+license = "MIT"
+description = "Embassy Hardware Abstraction Layer (HAL) for the IMXRT microcontroller"
+keywords = ["embedded", "async", "imxrt", "rt600", "embedded-hal"]
+categories = ["embedded", "hardware-support", "no-std", "asynchronous"]
+repository = "https://github.com/embassy-rs/embassy"
+documentation = "https://docs.embassy.dev/embassy-imxrt"
+
+[package.metadata.embassy_docs]
+src_base = "https://github.com/embassy-rs/embassy/blob/embassy-imxrt-v$VERSION/embassy-imxrt/src/"
+src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-imxrt/src/"
+features = ["defmt", "unstable-pac", "time", "time-driver-os-timer"]
+flavors = [
+ { regex_feature = "mimxrt6.*", target = "thumbv8m.main-none-eabihf" }
+]
+
+[package.metadata.docs.rs]
+features = ["mimxrt685s", "defmt", "unstable-pac", "time", "time-driver"]
+rustdoc-args = ["--cfg", "docsrs"]
+
+[features]
+default = ["rt"]
+
+## Cortex-M runtime (enabled by default)
+rt = [
+ "mimxrt685s-pac?/rt",
+ "mimxrt633s-pac?/rt",
+]
+
+## Enable defmt
+defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt", "mimxrt685s-pac?/defmt", "mimxrt633s-pac?/defmt"]
+
+## Enable features requiring `embassy-time`
+time = ["dep:embassy-time", "embassy-embedded-hal/time"]
+
+## Enable custom embassy time-driver implementation, using 32KHz RTC
+time-driver-rtc = ["_time-driver", "embassy-time-driver?/tick-hz-1_000"]
+
+## Enable custom embassy time-driver implementation, using 1MHz OS Timer
+time-driver-os-timer = ["_time-driver", "embassy-time-driver?/tick-hz-1_000_000"]
+
+_time-driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils", "embassy-embedded-hal/time"]
+
+## Reexport the PAC for the currently enabled chip at `embassy_imxrt::pac` (unstable)
+unstable-pac = []
+
+# Features starting with `_` are for internal use only. They're not intended
+# to be enabled by other crates, and are not covered by semver guarantees.
+
+_mimxrt685s = []
+_mimxrt633s = ["_espi"]
+
+# Peripherals
+_espi = []
+
+#! ### Chip selection features
+## MIMXRT685S
+mimxrt685s = ["mimxrt685s-pac", "_mimxrt685s"]
+## MIMXRT633S
+mimxrt633s = ["mimxrt633s-pac", "_mimxrt633s"]
+
+[dependencies]
+embassy-sync = { version = "0.7.0", path = "../embassy-sync" }
+embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true }
+embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true }
+embassy-time = { version = "0.4", path = "../embassy-time", optional = true }
+embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] }
+embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false }
+embassy-futures = { version = "0.1.1", path = "../embassy-futures" }
+
+defmt = { version = "1.0.1", optional = true }
+log = { version = "0.4.14", optional = true }
+nb = "1.0.0"
+cfg-if = "1.0.0"
+cortex-m-rt = ">=0.7.3,<0.8"
+cortex-m = "0.7.6"
+critical-section = "1.1"
+embedded-io = { version = "0.6.1" }
+embedded-io-async = { version = "0.6.1" }
+fixed = "1.23.1"
+
+rand-core-06 = { package = "rand_core", version = "0.6" }
+rand-core-09 = { package = "rand_core", version = "0.9" }
+
+embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
+ "unproven",
+] }
+embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
+embedded-hal-async = { version = "1.0" }
+embedded-hal-nb = { version = "1.0" }
+
+document-features = "0.2.7"
+paste = "1.0"
+
+# PACs
+mimxrt685s-pac = { version = "0.4.0", optional = true, features = ["rt", "critical-section"] }
+mimxrt633s-pac = { version = "0.4.0", optional = true, features = ["rt", "critical-section"] }
diff --git a/embassy-imxrt/README.md b/embassy-imxrt/README.md
new file mode 100644
index 000000000..cfdfb8ce2
--- /dev/null
+++ b/embassy-imxrt/README.md
@@ -0,0 +1,59 @@
+# Embassy iMXRT HAL
+
+## Introduction
+
+HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so
+raw register manipulation is not needed.
+
+The Embassy iMXRT HAL targets the NXP iMXRT Family of MCUs. The HAL implements
+both blocking and async APIs for many peripherals. The benefit of using the
+async APIs is that the HAL takes care of waiting for peripherals to complete
+operations in low power mode and handling of interrupts, so that applications
+can focus on business logic.
+
+NOTE: The Embassy HALs can be used both for non-async and async operations. For
+async, you can choose which runtime you want to use.
+
+For a complete list of available peripherals and features, see the
+[embassy-imxrt documentation](https://docs.embassy.dev/embassy-imxrt).
+
+## Hardware support
+
+The `embassy-imxrt` HAL currently supports two main variants of the iMXRT
+family:
+
+* MIMXRT685S
+ ([examples](https://github.com/OpenDevicePartnership/embassy-imxrt/tree/main/examples/rt685s-evk))
+* MIMXRT633s
+ ([examples](https://github.com/OpenDevicePartnership/embassy-imxrt/tree/main/examples/rt633))
+
+Several peripherals are supported and tested on both supported chip variants. To
+check what's available, make sure to the MCU you're targetting in the top menu
+in the [documentation](https://docs.embassy.dev/embassy-imxrt).
+
+## TrustZone support
+
+TrustZone support is yet to be implemented.
+
+## Time driver
+
+If the `time-driver` feature is enabled, the HAL uses the RTC peripheral as a
+global time driver for [embassy-time](https://crates.io/crates/embassy-time),
+with a tick rate of 32768 Hz.
+
+## Embedded-hal
+
+The `embassy-imxrt` HAL implements the traits from
+[embedded-hal](https://crates.io/crates/embedded-hal) (v0.2 and 1.0) and
+[embedded-hal-async](https://crates.io/crates/embedded-hal-async), as well as
+[embedded-io](https://crates.io/crates/embedded-io) and
+[embedded-io-async](https://crates.io/crates/embedded-io-async).
+
+## Interoperability
+
+This crate can run on any executor.
+
+Optionally, some features requiring
+[`embassy-time`](https://crates.io/crates/embassy-time) can be activated with
+the `time` feature. If you enable it, you must link an `embassy-time` driver in
+your project.
diff --git a/embassy-imxrt/src/chips/mimxrt633s.rs b/embassy-imxrt/src/chips/mimxrt633s.rs
new file mode 100644
index 000000000..30072132a
--- /dev/null
+++ b/embassy-imxrt/src/chips/mimxrt633s.rs
@@ -0,0 +1,389 @@
+pub use mimxrt633s_pac as pac;
+
+#[allow(clippy::missing_safety_doc)]
+pub mod interrupts {
+ embassy_hal_internal::interrupt_mod!(
+ ACMP,
+ ADC0,
+ CASPER,
+ CTIMER0,
+ CTIMER1,
+ CTIMER2,
+ CTIMER3,
+ CTIMER4,
+ DMA0,
+ DMA1,
+ DMIC0,
+ ESPI,
+ FLEXCOMM0,
+ FLEXCOMM1,
+ FLEXCOMM14,
+ FLEXCOMM15,
+ FLEXCOMM2,
+ FLEXCOMM3,
+ FLEXCOMM4,
+ FLEXCOMM5,
+ FLEXCOMM6,
+ FLEXCOMM7,
+ FLEXSPI,
+ GPIO_INTA,
+ GPIO_INTB,
+ HASHCRYPT,
+ HWVAD0,
+ HYPERVISOR,
+ I3C0,
+ MRT0,
+ MU_A,
+ OS_EVENT,
+ PIN_INT0,
+ PIN_INT1,
+ PIN_INT2,
+ PIN_INT3,
+ PIN_INT4,
+ PIN_INT5,
+ PIN_INT6,
+ PIN_INT7,
+ PMC_PMIC,
+ POWERQUAD,
+ PUF,
+ RNG,
+ RTC,
+ SCT0,
+ SECUREVIOLATION,
+ SGPIO_INTA,
+ SGPIO_INTB,
+ USB,
+ USBPHY_DCD,
+ USB_WAKEUP,
+ USDHC0,
+ USDHC1,
+ UTICK0,
+ WDT0,
+ WDT1,
+ );
+}
+
+embassy_hal_internal::peripherals!(
+ ACMP,
+ ADC0,
+ CASPER,
+ CRC,
+ CTIMER0_COUNT_CHANNEL0,
+ CTIMER0_COUNT_CHANNEL1,
+ CTIMER0_COUNT_CHANNEL2,
+ CTIMER0_COUNT_CHANNEL3,
+ CTIMER0_CAPTURE_CHANNEL0,
+ CTIMER0_CAPTURE_CHANNEL1,
+ CTIMER0_CAPTURE_CHANNEL2,
+ CTIMER0_CAPTURE_CHANNEL3,
+ CTIMER1_COUNT_CHANNEL0,
+ CTIMER1_COUNT_CHANNEL1,
+ CTIMER1_COUNT_CHANNEL2,
+ CTIMER1_COUNT_CHANNEL3,
+ CTIMER1_CAPTURE_CHANNEL0,
+ CTIMER1_CAPTURE_CHANNEL1,
+ CTIMER1_CAPTURE_CHANNEL2,
+ CTIMER1_CAPTURE_CHANNEL3,
+ CTIMER2_COUNT_CHANNEL0,
+ CTIMER2_COUNT_CHANNEL1,
+ CTIMER2_COUNT_CHANNEL2,
+ CTIMER2_COUNT_CHANNEL3,
+ CTIMER2_CAPTURE_CHANNEL0,
+ CTIMER2_CAPTURE_CHANNEL1,
+ CTIMER2_CAPTURE_CHANNEL2,
+ CTIMER2_CAPTURE_CHANNEL3,
+ CTIMER3_COUNT_CHANNEL0,
+ CTIMER3_COUNT_CHANNEL1,
+ CTIMER3_COUNT_CHANNEL2,
+ CTIMER3_COUNT_CHANNEL3,
+ CTIMER3_CAPTURE_CHANNEL0,
+ CTIMER3_CAPTURE_CHANNEL1,
+ CTIMER3_CAPTURE_CHANNEL2,
+ CTIMER3_CAPTURE_CHANNEL3,
+ CTIMER4_COUNT_CHANNEL0,
+ CTIMER4_COUNT_CHANNEL1,
+ CTIMER4_COUNT_CHANNEL2,
+ CTIMER4_COUNT_CHANNEL3,
+ CTIMER4_CAPTURE_CHANNEL0,
+ CTIMER4_CAPTURE_CHANNEL1,
+ CTIMER4_CAPTURE_CHANNEL2,
+ CTIMER4_CAPTURE_CHANNEL3,
+ DMA0,
+ DMA0_CH0,
+ DMA0_CH1,
+ DMA0_CH2,
+ DMA0_CH3,
+ DMA0_CH4,
+ DMA0_CH5,
+ DMA0_CH6,
+ DMA0_CH7,
+ DMA0_CH8,
+ DMA0_CH9,
+ DMA0_CH10,
+ DMA0_CH11,
+ DMA0_CH12,
+ DMA0_CH13,
+ DMA0_CH14,
+ DMA0_CH15,
+ DMA0_CH16,
+ DMA0_CH17,
+ DMA0_CH18,
+ DMA0_CH19,
+ DMA0_CH20,
+ DMA0_CH21,
+ DMA0_CH22,
+ DMA0_CH23,
+ DMA0_CH24,
+ DMA0_CH25,
+ DMA0_CH26,
+ DMA0_CH27,
+ DMA0_CH28,
+ DMA0_CH29,
+ DMA0_CH30,
+ DMA0_CH31,
+ DMA0_CH32,
+ DMA1,
+ DMA1_CH0,
+ DMA1_CH1,
+ DMA1_CH2,
+ DMA1_CH3,
+ DMA1_CH4,
+ DMA1_CH5,
+ DMA1_CH6,
+ DMA1_CH7,
+ DMA1_CH8,
+ DMA1_CH9,
+ DMA1_CH10,
+ DMA1_CH11,
+ DMA1_CH12,
+ DMA1_CH13,
+ DMA1_CH14,
+ DMA1_CH15,
+ DMA1_CH16,
+ DMA1_CH17,
+ DMA1_CH18,
+ DMA1_CH19,
+ DMA1_CH20,
+ DMA1_CH21,
+ DMA1_CH22,
+ DMA1_CH23,
+ DMA1_CH24,
+ DMA1_CH25,
+ DMA1_CH26,
+ DMA1_CH27,
+ DMA1_CH28,
+ DMA1_CH29,
+ DMA1_CH30,
+ DMA1_CH31,
+ DMA1_CH32,
+ DMIC0,
+ DSPWAKE,
+ ESPI,
+ FLEXCOMM0,
+ FLEXCOMM1,
+ FLEXCOMM14,
+ FLEXCOMM15,
+ FLEXCOMM2,
+ FLEXCOMM3,
+ FLEXCOMM4,
+ FLEXCOMM5,
+ FLEXCOMM6,
+ FLEXCOMM7,
+ FLEXSPI,
+ FREQME,
+ GPIO_INTA,
+ GPIO_INTB,
+ HASHCRYPT,
+ HSGPIO0,
+ HSGPIO1,
+ HSGPIO2,
+ HSGPIO3,
+ HSGPIO4,
+ HSGPIO5,
+ HSGPIO6,
+ HSGPIO7,
+ HWVAD0,
+ HYPERVISOR,
+ I3C0,
+ MRT0,
+ MU_A,
+ OS_EVENT,
+ PIN_INT0,
+ PIN_INT1,
+ PIN_INT2,
+ PIN_INT3,
+ PIN_INT4,
+ PIN_INT5,
+ PIN_INT6,
+ PIN_INT7,
+ PIO0_0,
+ PIO0_1,
+ PIO0_10,
+ PIO0_11,
+ PIO0_12,
+ PIO0_13,
+ PIO0_14,
+ PIO0_15,
+ PIO0_16,
+ PIO0_17,
+ PIO0_18,
+ PIO0_19,
+ PIO0_2,
+ PIO0_20,
+ PIO0_21,
+ PIO0_22,
+ PIO0_23,
+ PIO0_24,
+ PIO0_25,
+ PIO0_26,
+ PIO0_27,
+ PIO0_28,
+ PIO0_29,
+ PIO0_3,
+ PIO0_30,
+ PIO0_31,
+ PIO0_4,
+ PIO0_5,
+ PIO0_6,
+ PIO0_7,
+ PIO0_8,
+ PIO0_9,
+ PIO1_0,
+ PIO1_1,
+ PIO1_10,
+ PIO1_11,
+ PIO1_12,
+ PIO1_13,
+ PIO1_14,
+ PIO1_15,
+ PIO1_16,
+ PIO1_17,
+ PIO1_18,
+ PIO1_19,
+ PIO1_2,
+ PIO1_20,
+ PIO1_21,
+ PIO1_22,
+ PIO1_23,
+ PIO1_24,
+ PIO1_25,
+ PIO1_26,
+ PIO1_27,
+ PIO1_28,
+ PIO1_29,
+ PIO1_3,
+ PIO1_30,
+ PIO1_31,
+ PIO1_4,
+ PIO1_5,
+ PIO1_6,
+ PIO1_7,
+ PIO1_8,
+ PIO1_9,
+ PIO2_0,
+ PIO2_1,
+ PIO2_10,
+ PIO2_11,
+ PIO2_12,
+ PIO2_13,
+ PIO2_14,
+ PIO2_15,
+ PIO2_16,
+ PIO2_17,
+ PIO2_18,
+ PIO2_19,
+ PIO2_2,
+ PIO2_20,
+ PIO2_21,
+ PIO2_22,
+ PIO2_23,
+ PIO2_24,
+ PIO2_25,
+ PIO2_26,
+ PIO2_27,
+ PIO2_28,
+ PIO2_29,
+ PIO2_3,
+ PIO2_30,
+ PIO2_31,
+ PIO2_4,
+ PIO2_5,
+ PIO2_6,
+ PIO2_7,
+ PIO2_8,
+ PIO2_9,
+ PIO3_0,
+ PIO3_1,
+ PIO3_10,
+ PIO3_11,
+ PIO3_12,
+ PIO3_13,
+ PIO3_14,
+ PIO3_15,
+ PIO3_16,
+ PIO3_17,
+ PIO3_18,
+ PIO3_19,
+ PIO3_2,
+ PIO3_20,
+ PIO3_21,
+ PIO3_22,
+ PIO3_23,
+ PIO3_24,
+ PIO3_25,
+ PIO3_26,
+ PIO3_27,
+ PIO3_28,
+ PIO3_29,
+ PIO3_3,
+ PIO3_30,
+ PIO3_31,
+ PIO3_4,
+ PIO3_5,
+ PIO3_6,
+ PIO3_7,
+ PIO3_8,
+ PIO3_9,
+ PIO4_0,
+ PIO4_1,
+ PIO4_10,
+ PIO4_2,
+ PIO4_3,
+ PIO4_4,
+ PIO4_5,
+ PIO4_6,
+ PIO4_7,
+ PIO4_8,
+ PIO4_9,
+ PIO7_24,
+ PIO7_25,
+ PIO7_26,
+ PIO7_27,
+ PIO7_28,
+ PIO7_29,
+ PIO7_30,
+ PIO7_31,
+ PIOFC15_SCL,
+ PIOFC15_SDA,
+ PMC_PMIC,
+ PIMCTL,
+ POWERQUAD,
+ PUF,
+ RNG,
+ RTC,
+ SCT0,
+ SECGPIO,
+ SECUREVIOLATION,
+ SEMA42,
+ SGPIO_INTA,
+ SGPIO_INTB,
+ USBHSD,
+ USBHSH,
+ USBPHY,
+ USB_WAKEUP,
+ USDHC0,
+ USDHC1,
+ UTICK0,
+ WDT0,
+ WDT1,
+);
diff --git a/embassy-imxrt/src/chips/mimxrt685s.rs b/embassy-imxrt/src/chips/mimxrt685s.rs
new file mode 100644
index 000000000..746861e35
--- /dev/null
+++ b/embassy-imxrt/src/chips/mimxrt685s.rs
@@ -0,0 +1,388 @@
+pub use mimxrt685s_pac as pac;
+
+#[allow(clippy::missing_safety_doc)]
+pub mod interrupts {
+ embassy_hal_internal::interrupt_mod!(
+ ACMP,
+ ADC0,
+ CASPER,
+ CTIMER0,
+ CTIMER1,
+ CTIMER2,
+ CTIMER3,
+ CTIMER4,
+ DMA0,
+ DMA1,
+ DMIC0,
+ DSPWAKE,
+ FLEXCOMM0,
+ FLEXCOMM1,
+ FLEXCOMM14,
+ FLEXCOMM15,
+ FLEXCOMM2,
+ FLEXCOMM3,
+ FLEXCOMM4,
+ FLEXCOMM5,
+ FLEXCOMM6,
+ FLEXCOMM7,
+ FLEXSPI,
+ GPIO_INTA,
+ GPIO_INTB,
+ HASHCRYPT,
+ HWVAD0,
+ HYPERVISOR,
+ I3C0,
+ MRT0,
+ MU_A,
+ OS_EVENT,
+ PIN_INT0,
+ PIN_INT1,
+ PIN_INT2,
+ PIN_INT3,
+ PIN_INT4,
+ PIN_INT5,
+ PIN_INT6,
+ PIN_INT7,
+ PMC_PMIC,
+ POWERQUAD,
+ PUF,
+ RNG,
+ RTC,
+ SCT0,
+ SECUREVIOLATION,
+ SGPIO_INTA,
+ SGPIO_INTB,
+ USB,
+ USBPHY_DCD,
+ USB_WAKEUP,
+ USDHC0,
+ USDHC1,
+ UTICK0,
+ WDT0,
+ WDT1,
+ );
+}
+
+embassy_hal_internal::peripherals!(
+ ACMP,
+ ADC0,
+ CASPER,
+ CRC,
+ CTIMER0_COUNT_CHANNEL0,
+ CTIMER0_COUNT_CHANNEL1,
+ CTIMER0_COUNT_CHANNEL2,
+ CTIMER0_COUNT_CHANNEL3,
+ CTIMER0_CAPTURE_CHANNEL0,
+ CTIMER0_CAPTURE_CHANNEL1,
+ CTIMER0_CAPTURE_CHANNEL2,
+ CTIMER0_CAPTURE_CHANNEL3,
+ CTIMER1_COUNT_CHANNEL0,
+ CTIMER1_COUNT_CHANNEL1,
+ CTIMER1_COUNT_CHANNEL2,
+ CTIMER1_COUNT_CHANNEL3,
+ CTIMER1_CAPTURE_CHANNEL0,
+ CTIMER1_CAPTURE_CHANNEL1,
+ CTIMER1_CAPTURE_CHANNEL2,
+ CTIMER1_CAPTURE_CHANNEL3,
+ CTIMER2_COUNT_CHANNEL0,
+ CTIMER2_COUNT_CHANNEL1,
+ CTIMER2_COUNT_CHANNEL2,
+ CTIMER2_COUNT_CHANNEL3,
+ CTIMER2_CAPTURE_CHANNEL0,
+ CTIMER2_CAPTURE_CHANNEL1,
+ CTIMER2_CAPTURE_CHANNEL2,
+ CTIMER2_CAPTURE_CHANNEL3,
+ CTIMER3_COUNT_CHANNEL0,
+ CTIMER3_COUNT_CHANNEL1,
+ CTIMER3_COUNT_CHANNEL2,
+ CTIMER3_COUNT_CHANNEL3,
+ CTIMER3_CAPTURE_CHANNEL0,
+ CTIMER3_CAPTURE_CHANNEL1,
+ CTIMER3_CAPTURE_CHANNEL2,
+ CTIMER3_CAPTURE_CHANNEL3,
+ CTIMER4_COUNT_CHANNEL0,
+ CTIMER4_COUNT_CHANNEL1,
+ CTIMER4_COUNT_CHANNEL2,
+ CTIMER4_COUNT_CHANNEL3,
+ CTIMER4_CAPTURE_CHANNEL0,
+ CTIMER4_CAPTURE_CHANNEL1,
+ CTIMER4_CAPTURE_CHANNEL2,
+ CTIMER4_CAPTURE_CHANNEL3,
+ DMA0,
+ DMA0_CH0,
+ DMA0_CH1,
+ DMA0_CH2,
+ DMA0_CH3,
+ DMA0_CH4,
+ DMA0_CH5,
+ DMA0_CH6,
+ DMA0_CH7,
+ DMA0_CH8,
+ DMA0_CH9,
+ DMA0_CH10,
+ DMA0_CH11,
+ DMA0_CH12,
+ DMA0_CH13,
+ DMA0_CH14,
+ DMA0_CH15,
+ DMA0_CH16,
+ DMA0_CH17,
+ DMA0_CH18,
+ DMA0_CH19,
+ DMA0_CH20,
+ DMA0_CH21,
+ DMA0_CH22,
+ DMA0_CH23,
+ DMA0_CH24,
+ DMA0_CH25,
+ DMA0_CH26,
+ DMA0_CH27,
+ DMA0_CH28,
+ DMA0_CH29,
+ DMA0_CH30,
+ DMA0_CH31,
+ DMA0_CH32,
+ DMA1,
+ DMA1_CH0,
+ DMA1_CH1,
+ DMA1_CH2,
+ DMA1_CH3,
+ DMA1_CH4,
+ DMA1_CH5,
+ DMA1_CH6,
+ DMA1_CH7,
+ DMA1_CH8,
+ DMA1_CH9,
+ DMA1_CH10,
+ DMA1_CH11,
+ DMA1_CH12,
+ DMA1_CH13,
+ DMA1_CH14,
+ DMA1_CH15,
+ DMA1_CH16,
+ DMA1_CH17,
+ DMA1_CH18,
+ DMA1_CH19,
+ DMA1_CH20,
+ DMA1_CH21,
+ DMA1_CH22,
+ DMA1_CH23,
+ DMA1_CH24,
+ DMA1_CH25,
+ DMA1_CH26,
+ DMA1_CH27,
+ DMA1_CH28,
+ DMA1_CH29,
+ DMA1_CH30,
+ DMA1_CH31,
+ DMA1_CH32,
+ DMIC0,
+ DSPWAKE,
+ FLEXCOMM0,
+ FLEXCOMM1,
+ FLEXCOMM14,
+ FLEXCOMM15,
+ FLEXCOMM2,
+ FLEXCOMM3,
+ FLEXCOMM4,
+ FLEXCOMM5,
+ FLEXCOMM6,
+ FLEXCOMM7,
+ FLEXSPI,
+ FREQME,
+ GPIO_INTA,
+ GPIO_INTB,
+ HASHCRYPT,
+ HSGPIO0,
+ HSGPIO1,
+ HSGPIO2,
+ HSGPIO3,
+ HSGPIO4,
+ HSGPIO5,
+ HSGPIO6,
+ HSGPIO7,
+ HWVAD0,
+ HYPERVISOR,
+ I3C0,
+ MRT0,
+ MU_A,
+ OS_EVENT,
+ PIN_INT0,
+ PIN_INT1,
+ PIN_INT2,
+ PIN_INT3,
+ PIN_INT4,
+ PIN_INT5,
+ PIN_INT6,
+ PIN_INT7,
+ PIO0_0,
+ PIO0_1,
+ PIO0_10,
+ PIO0_11,
+ PIO0_12,
+ PIO0_13,
+ PIO0_14,
+ PIO0_15,
+ PIO0_16,
+ PIO0_17,
+ PIO0_18,
+ PIO0_19,
+ PIO0_2,
+ PIO0_20,
+ PIO0_21,
+ PIO0_22,
+ PIO0_23,
+ PIO0_24,
+ PIO0_25,
+ PIO0_26,
+ PIO0_27,
+ PIO0_28,
+ PIO0_29,
+ PIO0_3,
+ PIO0_30,
+ PIO0_31,
+ PIO0_4,
+ PIO0_5,
+ PIO0_6,
+ PIO0_7,
+ PIO0_8,
+ PIO0_9,
+ PIO1_0,
+ PIO1_1,
+ PIO1_10,
+ PIO1_11,
+ PIO1_12,
+ PIO1_13,
+ PIO1_14,
+ PIO1_15,
+ PIO1_16,
+ PIO1_17,
+ PIO1_18,
+ PIO1_19,
+ PIO1_2,
+ PIO1_20,
+ PIO1_21,
+ PIO1_22,
+ PIO1_23,
+ PIO1_24,
+ PIO1_25,
+ PIO1_26,
+ PIO1_27,
+ PIO1_28,
+ PIO1_29,
+ PIO1_3,
+ PIO1_30,
+ PIO1_31,
+ PIO1_4,
+ PIO1_5,
+ PIO1_6,
+ PIO1_7,
+ PIO1_8,
+ PIO1_9,
+ PIO2_0,
+ PIO2_1,
+ PIO2_10,
+ PIO2_11,
+ PIO2_12,
+ PIO2_13,
+ PIO2_14,
+ PIO2_15,
+ PIO2_16,
+ PIO2_17,
+ PIO2_18,
+ PIO2_19,
+ PIO2_2,
+ PIO2_20,
+ PIO2_21,
+ PIO2_22,
+ PIO2_23,
+ PIO2_24,
+ PIO2_25,
+ PIO2_26,
+ PIO2_27,
+ PIO2_28,
+ PIO2_29,
+ PIO2_3,
+ PIO2_30,
+ PIO2_31,
+ PIO2_4,
+ PIO2_5,
+ PIO2_6,
+ PIO2_7,
+ PIO2_8,
+ PIO2_9,
+ PIO3_0,
+ PIO3_1,
+ PIO3_10,
+ PIO3_11,
+ PIO3_12,
+ PIO3_13,
+ PIO3_14,
+ PIO3_15,
+ PIO3_16,
+ PIO3_17,
+ PIO3_18,
+ PIO3_19,
+ PIO3_2,
+ PIO3_20,
+ PIO3_21,
+ PIO3_22,
+ PIO3_23,
+ PIO3_24,
+ PIO3_25,
+ PIO3_26,
+ PIO3_27,
+ PIO3_28,
+ PIO3_29,
+ PIO3_3,
+ PIO3_30,
+ PIO3_31,
+ PIO3_4,
+ PIO3_5,
+ PIO3_6,
+ PIO3_7,
+ PIO3_8,
+ PIO3_9,
+ PIO4_0,
+ PIO4_1,
+ PIO4_10,
+ PIO4_2,
+ PIO4_3,
+ PIO4_4,
+ PIO4_5,
+ PIO4_6,
+ PIO4_7,
+ PIO4_8,
+ PIO4_9,
+ PIO7_24,
+ PIO7_25,
+ PIO7_26,
+ PIO7_27,
+ PIO7_28,
+ PIO7_29,
+ PIO7_30,
+ PIO7_31,
+ PIOFC15_SCL,
+ PIOFC15_SDA,
+ PMC_PMIC,
+ PIMCTL,
+ POWERQUAD,
+ PUF,
+ RNG,
+ RTC,
+ SCT0,
+ SECGPIO,
+ SECUREVIOLATION,
+ SEMA42,
+ SGPIO_INTA,
+ SGPIO_INTB,
+ USBHSD,
+ USBHSH,
+ USBPHY,
+ USB_WAKEUP,
+ USDHC0,
+ USDHC1,
+ UTICK0,
+ WDT0,
+ WDT1,
+);
diff --git a/embassy-imxrt/src/clocks.rs b/embassy-imxrt/src/clocks.rs
new file mode 100644
index 000000000..39c3e6238
--- /dev/null
+++ b/embassy-imxrt/src/clocks.rs
@@ -0,0 +1,1661 @@
+//! Clock configuration for the `RT6xx`
+use core::sync::atomic::{AtomicU32, AtomicU8, Ordering};
+
+use paste::paste;
+
+use crate::pac;
+
+/// Clock configuration;
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum Clocks {
+ /// Low power oscillator
+ Lposc,
+ /// System Frequency Resonance Oscillator (SFRO)
+ Sfro,
+ /// Real Time Clock
+ Rtc,
+ /// Feed-forward Ring Oscillator
+ Ffro, // This includes that div2 and div4 variations
+ /// External Clock Input
+ ClkIn,
+ /// AHB Clock
+ Hclk,
+ /// Main Clock
+ MainClk,
+ /// Main PLL Clock
+ MainPllClk, // also has aux0,aux1,dsp, and audio pll's downstream
+ /// System Clock
+ SysClk,
+ /// System Oscillator
+ SysOscClk,
+ /// ADC Clock
+ Adc,
+}
+
+/// Clock configuration.
+pub struct ClockConfig {
+ /// low-power oscillator config
+ pub lposc: LposcConfig,
+ /// 16Mhz internal oscillator config
+ pub sfro: SfroConfig,
+ /// Real Time Clock config
+ pub rtc: RtcClkConfig,
+ /// 48/60 Mhz internal oscillator config
+ pub ffro: FfroConfig,
+ // pub pll: Option, //potentially covered in main pll clk
+ /// External Clock-In config
+ pub clk_in: ClkInConfig,
+ /// AHB bus clock config
+ pub hclk: HclkConfig,
+ /// Main Clock config
+ pub main_clk: MainClkConfig,
+ /// Main Pll clock config
+ pub main_pll_clk: MainPllClkConfig,
+ /// Software concept to be used with systick, doesn't map to a register
+ pub sys_clk: SysClkConfig,
+ /// System Oscillator Config
+ pub sys_osc: SysOscConfig,
+ // todo: move ADC here
+}
+
+impl ClockConfig {
+ /// Clock configuration derived from external crystal.
+ #[must_use]
+ pub fn crystal() -> Self {
+ const CORE_CPU_FREQ: u32 = 500_000_000;
+ const PLL_CLK_FREQ: u32 = 528_000_000;
+ const SYS_CLK_FREQ: u32 = CORE_CPU_FREQ / 2;
+ Self {
+ lposc: LposcConfig {
+ state: State::Enabled,
+ freq: AtomicU32::new(Into::into(LposcFreq::Lp1m)),
+ },
+ sfro: SfroConfig { state: State::Enabled },
+ rtc: RtcClkConfig {
+ state: State::Enabled,
+ wake_alarm_state: State::Disabled,
+ sub_second_state: State::Disabled,
+ freq: AtomicU32::new(Into::into(RtcFreq::Default1Hz)),
+ rtc_int: RtcInterrupts::None,
+ },
+ ffro: FfroConfig {
+ state: State::Enabled,
+ freq: AtomicU32::new(Into::into(FfroFreq::Ffro48m)),
+ },
+ //pll: Some(PllConfig {}),//includes aux0 and aux1 pll
+ clk_in: ClkInConfig {
+ state: State::Disabled,
+ // This is an externally sourced clock
+ // Don't give it an initial frequency
+ freq: Some(AtomicU32::new(0)),
+ },
+ hclk: HclkConfig { state: State::Disabled },
+ main_clk: MainClkConfig {
+ state: State::Enabled,
+ //FFRO divided by 4 is reset values of Main Clk Sel A, Sel B
+ src: MainClkSrc::FFRO,
+ div_int: AtomicU32::new(4),
+ freq: AtomicU32::new(CORE_CPU_FREQ),
+ },
+ main_pll_clk: MainPllClkConfig {
+ state: State::Enabled,
+ src: MainPllClkSrc::SFRO,
+ freq: AtomicU32::new(PLL_CLK_FREQ),
+ mult: AtomicU8::new(16),
+ pfd0: 19, //
+ pfd1: 0, // future field
+ pfd2: 19, // 0x13
+ pfd3: 0, // future field
+ aux0_div: 0,
+ aux1_div: 0,
+ },
+ sys_clk: SysClkConfig {
+ sysclkfreq: AtomicU32::new(SYS_CLK_FREQ),
+ },
+ sys_osc: SysOscConfig { state: State::Enabled },
+ //adc: Some(AdcConfig {}), // TODO: add config
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+/// Clock state enum
+pub enum State {
+ /// Clock is enabled
+ Enabled,
+ /// Clock is disabled
+ Disabled,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+/// Low Power Oscillator valid frequencies
+pub enum LposcFreq {
+ /// 1 `MHz` oscillator
+ Lp1m,
+ /// 32kHz oscillator
+ Lp32k,
+}
+
+impl From for u32 {
+ fn from(value: LposcFreq) -> Self {
+ match value {
+ LposcFreq::Lp1m => 1_000_000,
+ LposcFreq::Lp32k => 32_768,
+ }
+ }
+}
+
+impl TryFrom for LposcFreq {
+ type Error = ClockError;
+ fn try_from(value: u32) -> Result {
+ match value {
+ 1_000_000 => Ok(LposcFreq::Lp1m),
+ 32_768 => Ok(LposcFreq::Lp32k),
+ _ => Err(ClockError::InvalidFrequency),
+ }
+ }
+}
+
+/// Low power oscillator config
+pub struct LposcConfig {
+ state: State,
+ // low power osc
+ freq: AtomicU32,
+}
+
+const SFRO_FREQ: u32 = 16_000_000;
+/// SFRO config
+pub struct SfroConfig {
+ state: State,
+}
+
+/// Valid RTC frequencies
+pub enum RtcFreq {
+ /// "Alarm" aka 1Hz clock
+ Default1Hz,
+ /// "Wake" aka 1kHz clock
+ HighResolution1khz,
+ /// 32kHz clock
+ SubSecond32kHz,
+}
+
+impl From for u32 {
+ fn from(value: RtcFreq) -> Self {
+ match value {
+ RtcFreq::Default1Hz => 1,
+ RtcFreq::HighResolution1khz => 1_000,
+ RtcFreq::SubSecond32kHz => 32_768,
+ }
+ }
+}
+
+impl TryFrom for RtcFreq {
+ type Error = ClockError;
+ fn try_from(value: u32) -> Result {
+ match value {
+ 1 => Ok(RtcFreq::Default1Hz),
+ 1_000 => Ok(RtcFreq::HighResolution1khz),
+ 32_768 => Ok(RtcFreq::SubSecond32kHz),
+ _ => Err(ClockError::InvalidFrequency),
+ }
+ }
+}
+
+/// RTC Interrupt options
+pub enum RtcInterrupts {
+ /// No interrupts are set
+ None,
+ /// 1Hz RTC clock aka Alarm interrupt set
+ Alarm,
+ /// 1kHz RTC clock aka Wake interrupt set
+ Wake,
+}
+
+impl From for u8 {
+ fn from(value: RtcInterrupts) -> Self {
+ match value {
+ RtcInterrupts::None => 0b00,
+ RtcInterrupts::Alarm => 0b01,
+ RtcInterrupts::Wake => 0b10,
+ }
+ }
+}
+/// RTC clock config.
+pub struct RtcClkConfig {
+ /// 1 Hz Clock state
+ pub state: State,
+ /// 1kHz Clock state
+ pub wake_alarm_state: State,
+ /// 32kHz Clock state
+ pub sub_second_state: State,
+ /// RTC clock source.
+ pub freq: AtomicU32,
+ /// RTC Interrupt
+ pub rtc_int: RtcInterrupts,
+}
+
+/// Valid FFRO Frequencies
+pub enum FfroFreq {
+ /// 48 Mhz Internal Oscillator
+ Ffro48m,
+ /// 60 `MHz` Internal Oscillator
+ Ffro60m,
+}
+
+/// FFRO Clock Config
+pub struct FfroConfig {
+ /// FFRO Clock state
+ state: State,
+ /// FFRO Frequency
+ freq: AtomicU32,
+}
+
+impl From for u32 {
+ fn from(value: FfroFreq) -> Self {
+ match value {
+ FfroFreq::Ffro48m => 48_000_000,
+ FfroFreq::Ffro60m => 60_000_000,
+ }
+ }
+}
+
+impl TryFrom for FfroFreq {
+ type Error = ClockError;
+ fn try_from(value: u32) -> Result {
+ match value {
+ 48_000_000 => Ok(FfroFreq::Ffro48m),
+ 60_000_000 => Ok(FfroFreq::Ffro60m),
+ _ => Err(ClockError::InvalidFrequency),
+ }
+ }
+}
+
+/// PLL clock source
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum MainPllClkSrc {
+ /// SFRO
+ SFRO,
+ /// External Clock
+ ClkIn,
+ /// FFRO
+ FFRO,
+}
+
+/// Transform from Source Clock enum to Clocks
+impl From for Clocks {
+ fn from(value: MainPllClkSrc) -> Self {
+ match value {
+ MainPllClkSrc::SFRO => Clocks::Sfro,
+ MainPllClkSrc::ClkIn => Clocks::ClkIn,
+ MainPllClkSrc::FFRO => Clocks::Ffro,
+ }
+ }
+}
+
+impl TryFrom for MainPllClkSrc {
+ type Error = ClockError;
+ fn try_from(value: Clocks) -> Result {
+ match value {
+ Clocks::Sfro => Ok(MainPllClkSrc::SFRO),
+ Clocks::Ffro => Ok(MainPllClkSrc::FFRO),
+ Clocks::ClkIn => Ok(MainPllClkSrc::ClkIn),
+ _ => Err(ClockError::ClockNotSupported),
+ }
+ }
+}
+
+/// PLL configuration.
+pub struct MainPllClkConfig {
+ /// Clock active state
+ pub state: State,
+ /// Main clock source.
+ pub src: MainPllClkSrc,
+ /// Main clock frequency
+ pub freq: AtomicU32,
+ //TODO: numerator and denominator not used but present in register
+ /// Multiplication factor.
+ pub mult: AtomicU8,
+ // the following are actually 6-bits not 8
+ /// Fractional divider 0, main pll clock
+ pub pfd0: u8,
+ /// Fractional divider 1
+ pub pfd1: u8,
+ /// Fractional divider 2
+ pub pfd2: u8,
+ /// Fractional divider 3
+ pub pfd3: u8,
+ // Aux dividers
+ /// aux divider 0
+ pub aux0_div: u8,
+ /// aux divider 1
+ pub aux1_div: u8,
+}
+/// External input clock config
+pub struct ClkInConfig {
+ /// External clock input state
+ state: State,
+ /// External clock input rate
+ freq: Option,
+}
+
+/// AHB clock config
+pub struct HclkConfig {
+ /// divider to turn main clk into hclk for AHB bus
+ pub state: State,
+}
+
+/// Main clock source.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum MainClkSrc {
+ /// FFRO divided by 4
+ FFROdiv4, // probably don't need since it'll be covered by div_int
+ /// External Clock
+ ClkIn,
+ /// Low Power Oscillator
+ Lposc,
+ /// FFRO
+ FFRO,
+ /// SFRO
+ SFRO,
+ /// Main PLL Clock
+ PllMain,
+ /// RTC 32kHz oscillator.
+ RTC32k,
+}
+
+impl From for Clocks {
+ fn from(value: MainClkSrc) -> Self {
+ match value {
+ MainClkSrc::ClkIn => Clocks::ClkIn,
+ MainClkSrc::Lposc => Clocks::Lposc,
+ MainClkSrc::FFRO => Clocks::Ffro,
+ MainClkSrc::SFRO => Clocks::Sfro,
+ MainClkSrc::PllMain => Clocks::MainPllClk,
+ MainClkSrc::RTC32k => Clocks::Rtc,
+ MainClkSrc::FFROdiv4 => Clocks::Ffro,
+ }
+ }
+}
+
+impl TryFrom for MainClkSrc {
+ type Error = ClockError;
+ fn try_from(value: Clocks) -> Result {
+ match value {
+ Clocks::ClkIn => Ok(MainClkSrc::ClkIn),
+ Clocks::Lposc => Ok(MainClkSrc::Lposc),
+ Clocks::Sfro => Ok(MainClkSrc::SFRO),
+ Clocks::MainPllClk => Ok(MainClkSrc::PllMain),
+ Clocks::Rtc => Ok(MainClkSrc::RTC32k),
+ Clocks::Ffro => Ok(MainClkSrc::FFRO),
+ _ => Err(ClockError::ClockNotSupported),
+ }
+ }
+}
+
+/// Main clock config.
+pub struct MainClkConfig {
+ /// Main clock state
+ pub state: State,
+ /// Main clock source.
+ pub src: MainClkSrc,
+ /// Main clock divider.
+ pub div_int: AtomicU32,
+ /// Clock Frequency
+ pub freq: AtomicU32,
+}
+
+/// System Core Clock config, SW concept for systick
+pub struct SysClkConfig {
+ /// keeps track of the system core clock frequency
+ /// future use with systick
+ pub sysclkfreq: AtomicU32,
+}
+
+/// System Oscillator Config
+pub struct SysOscConfig {
+ /// Clock State
+ pub state: State,
+}
+const SYS_OSC_DEFAULT_FREQ: u32 = 24_000_000;
+
+/// Clock Errors
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum ClockError {
+ /// Error due to attempting to change a clock with the wrong config block
+ ClockMismatch,
+ /// Error due to attempting to modify a clock that's not yet been enabled
+ ClockNotEnabled,
+ /// Error due to attempting to set a clock source that's not a supported option
+ ClockNotSupported,
+ /// Error due to attempting to set a clock to an invalid frequency
+ InvalidFrequency,
+ /// Error due to attempting to modify a clock output with an invalid divider
+ InvalidDiv,
+ /// Error due to attempting to modify a clock output with an invalid multiplier
+ InvalidMult,
+}
+
+/// Trait to configure one of the clocks
+pub trait ConfigurableClock {
+ /// Reset the clock, will enable it
+ fn disable(&self) -> Result<(), ClockError>;
+ /// Enable the clock
+ fn enable_and_reset(&self) -> Result<(), ClockError>;
+ /// Return the clock rate (Hz)
+ fn get_clock_rate(&self) -> Result;
+ /// Set the desired clock rate (Hz)
+ fn set_clock_rate(&mut self, div: u8, mult: u8, freq: u32) -> Result<(), ClockError>;
+ /// Returns whether this clock is enabled
+ fn is_enabled(&self) -> bool;
+}
+
+impl LposcConfig {
+ /// Initializes low-power oscillator.
+ fn init_lposc() {
+ // Enable low power oscillator
+ // SAFETY: unsafe needed to take pointer to Sysctl0, only happens once during init
+ let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };
+ sysctl0.pdruncfg0_clr().write(|w| w.lposc_pd().clr_pdruncfg0());
+
+ // Wait for low-power oscillator to be ready (typically 64 us)
+ // Busy loop seems better here than trying to shoe-in an async delay
+ // SAFETY: unsafe needed to take pointer to Clkctl0, needed to validate HW is ready
+ let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };
+ while clkctl0.lposcctl0().read().clkrdy().bit_is_clear() {}
+ }
+}
+impl ConfigurableClock for LposcConfig {
+ fn enable_and_reset(&self) -> Result<(), ClockError> {
+ LposcConfig::init_lposc();
+ Ok(())
+ }
+ fn disable(&self) -> Result<(), ClockError> {
+ // SAFETY: unsafe needed to take pointer to Sysctl0, needed to power down the LPOSC HW
+ let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };
+ sysctl0.pdruncfg0_set().write(|w| w.lposc_pd().set_pdruncfg0());
+ // Wait until LPOSC disabled
+ while !sysctl0.pdruncfg0().read().lposc_pd().is_power_down() {}
+ Ok(())
+ }
+ fn get_clock_rate(&self) -> Result {
+ Ok(self.freq.load(Ordering::Relaxed))
+ }
+ fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {
+ if let Ok(r) = >::try_into(freq) {
+ match r {
+ LposcFreq::Lp1m => {
+ self.freq
+ .store(LposcFreq::Lp1m as u32, core::sync::atomic::Ordering::Relaxed);
+ Ok(())
+ }
+ LposcFreq::Lp32k => {
+ self.freq
+ .store(LposcFreq::Lp1m as u32, core::sync::atomic::Ordering::Relaxed);
+ Ok(())
+ }
+ }
+ } else {
+ Err(ClockError::InvalidFrequency)
+ }
+ }
+ fn is_enabled(&self) -> bool {
+ self.state == State::Enabled
+ }
+}
+
+impl FfroConfig {
+ /// Necessary register writes to initialize the FFRO clock
+ pub fn init_ffro_clk() {
+ // SAFETY: unsafe needed to take pointer to Sysctl0, only to power up FFRO
+ let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };
+
+ /* Power on FFRO (48/60MHz) */
+ sysctl0.pdruncfg0_clr().write(|w| w.ffro_pd().clr_pdruncfg0());
+
+ // SAFETY: unsafe needed to take pointer to Clkctl0, only to set proper ffro update mode
+ let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };
+
+ clkctl0.ffroctl1().write(|w| w.update().normal_mode());
+
+ // No FFRO enable/disable control in CLKCTL.
+ // Delay enough for FFRO to be stable in case it was just powered on
+ delay_loop_clocks(50, 12_000_000);
+ }
+}
+
+impl ConfigurableClock for FfroConfig {
+ fn enable_and_reset(&self) -> Result<(), ClockError> {
+ // SAFETY: should be called once
+ FfroConfig::init_ffro_clk();
+ // default is 48 MHz
+ Ok(())
+ }
+ fn disable(&self) -> Result<(), ClockError> {
+ // SAFETY: unsafe needed to take pointer to Sysctl0, only to power down FFRO
+ let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };
+ sysctl0.pdruncfg0_set().write(|w| w.ffro_pd().set_pdruncfg0());
+ delay_loop_clocks(30, 12_000_000);
+ // Wait until FFRO disabled
+ while !sysctl0.pdruncfg0().read().ffro_pd().is_power_down() {}
+ Ok(())
+ }
+ fn get_clock_rate(&self) -> Result {
+ Ok(self.freq.load(Ordering::Relaxed))
+ }
+ fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {
+ if let Ok(r) = >::try_into(freq) {
+ match r {
+ FfroFreq::Ffro48m => {
+ // SAFETY: unsafe needed to take pointer to Clkctl0, needed to set the right HW frequency
+ let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };
+ clkctl0.ffroctl1().write(|w| w.update().update_safe_mode());
+ clkctl0.ffroctl0().write(|w| w.trim_range().ffro_48mhz());
+ clkctl0.ffroctl1().write(|w| w.update().normal_mode());
+
+ self.freq
+ .store(FfroFreq::Ffro48m as u32, core::sync::atomic::Ordering::Relaxed);
+ Ok(())
+ }
+ FfroFreq::Ffro60m => {
+ // SAFETY: unsafe needed to take pointer to Clkctl0, needed to set the right HW frequency
+ let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };
+ clkctl0.ffroctl1().write(|w| w.update().update_safe_mode());
+ clkctl0.ffroctl0().write(|w| w.trim_range().ffro_60mhz());
+ clkctl0.ffroctl1().write(|w| w.update().normal_mode());
+
+ self.freq
+ .store(FfroFreq::Ffro60m as u32, core::sync::atomic::Ordering::Relaxed);
+ Ok(())
+ }
+ }
+ } else {
+ error!("failed to convert desired clock rate, {:#}, to FFRO Freq", freq);
+ Err(ClockError::InvalidFrequency)
+ }
+ }
+ fn is_enabled(&self) -> bool {
+ self.state == State::Enabled
+ }
+}
+
+impl ConfigurableClock for SfroConfig {
+ fn enable_and_reset(&self) -> Result<(), ClockError> {
+ // SAFETY: unsafe needed to take pointer to Sysctl0, only to power up SFRO
+ let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };
+ sysctl0.pdruncfg0_clr().write(|w| w.sfro_pd().clr_pdruncfg0());
+ // wait until ready
+ while !sysctl0.pdruncfg0().read().sfro_pd().is_enabled() {}
+ Ok(())
+ }
+ fn disable(&self) -> Result<(), ClockError> {
+ // SAFETY: unsafe needed to take pointer to Sysctl0, only to power down SFRO
+ let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };
+ sysctl0.pdruncfg0_set().write(|w| w.sfro_pd().set_pdruncfg0());
+ delay_loop_clocks(30, 12_000_000);
+ // Wait until SFRO disabled
+ while !sysctl0.pdruncfg0().read().sfro_pd().is_power_down() {}
+ Ok(())
+ }
+ fn get_clock_rate(&self) -> Result {
+ if self.state == State::Enabled {
+ Ok(SFRO_FREQ)
+ } else {
+ Err(ClockError::ClockNotEnabled)
+ }
+ }
+ fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {
+ if self.state == State::Enabled {
+ if freq == SFRO_FREQ {
+ Ok(())
+ } else {
+ Err(ClockError::InvalidFrequency)
+ }
+ } else {
+ Err(ClockError::ClockNotEnabled)
+ }
+ }
+ fn is_enabled(&self) -> bool {
+ self.state == State::Enabled
+ }
+}
+
+/// A Clock with multiple options for clock source
+pub trait MultiSourceClock {
+ /// Returns which clock is being used as the clock source and its rate
+ fn get_clock_source_and_rate(&self, clock: &Clocks) -> Result<(Clocks, u32), ClockError>;
+ /// Sets a specific clock source and its associated rate
+ fn set_clock_source_and_rate(
+ &mut self,
+ clock_src_config: &mut impl ConfigurableClock,
+ clock_src: &Clocks,
+ rate: u32,
+ ) -> Result<(), ClockError>;
+}
+
+impl MultiSourceClock for MainPllClkConfig {
+ fn get_clock_source_and_rate(&self, clock: &Clocks) -> Result<(Clocks, u32), ClockError> {
+ match clock {
+ Clocks::MainPllClk => {
+ let converted_clock = Clocks::from(self.src);
+ Ok((converted_clock, self.freq.load(Ordering::Relaxed)))
+ }
+ _ => Err(ClockError::ClockMismatch),
+ }
+ }
+ fn set_clock_source_and_rate(
+ &mut self,
+ clock_src_config: &mut impl ConfigurableClock,
+ clock_src: &Clocks,
+ rate: u32,
+ ) -> Result<(), ClockError> {
+ if let Ok(c) = >::try_into(*clock_src) {
+ match c {
+ MainPllClkSrc::ClkIn => {
+ self.src = MainPllClkSrc::ClkIn;
+ // div mult and rate don't matter since this is an external clock
+ self.set_clock_rate(1, 1, rate)
+ }
+ MainPllClkSrc::FFRO => {
+ // FFRO Clock is divided by 2
+ let r = clock_src_config.get_clock_rate()?;
+ let base_rate = r / 2;
+ let m = MainPllClkConfig::calc_mult(rate, base_rate)?;
+
+ self.src = MainPllClkSrc::FFRO;
+ self.set_clock_rate(2, m, rate)
+ }
+ MainPllClkSrc::SFRO => {
+ if !clock_src_config.is_enabled() {
+ return Err(ClockError::ClockNotEnabled);
+ }
+ // check if desired frequency is a valid multiple of 16m SFRO clock
+ let m = MainPllClkConfig::calc_mult(rate, SFRO_FREQ)?;
+ self.src = MainPllClkSrc::SFRO;
+ self.set_clock_rate(1, m, rate)
+ }
+ }
+ } else {
+ Err(ClockError::ClockNotSupported)
+ }
+ }
+}
+
+impl ConfigurableClock for MainPllClkConfig {
+ fn enable_and_reset(&self) -> Result<(), ClockError> {
+ MainPllClkConfig::init_syspll();
+
+ MainPllClkConfig::init_syspll_pfd0(self.pfd0);
+
+ MainPllClkConfig::init_syspll_pfd2(self.pfd2);
+ Ok(())
+ }
+ fn disable(&self) -> Result<(), ClockError> {
+ if self.is_enabled() {
+ Err(ClockError::ClockNotSupported)
+ } else {
+ Err(ClockError::ClockNotEnabled)
+ }
+ }
+ fn get_clock_rate(&self) -> Result {
+ if self.is_enabled() {
+ let (_c, rate) = self.get_clock_source_and_rate(&Clocks::MainPllClk)?;
+ Ok(rate)
+ } else {
+ Err(ClockError::ClockNotEnabled)
+ }
+ }
+ fn set_clock_rate(&mut self, div: u8, mult: u8, freq: u32) -> Result<(), ClockError> {
+ if self.is_enabled() {
+ // SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0
+ let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };
+ let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };
+
+ // Power down pll before changes
+ sysctl0
+ .pdruncfg0_set()
+ .write(|w| w.syspllldo_pd().set_pdruncfg0().syspllana_pd().set_pdruncfg0());
+
+ let desired_freq: u64 = self.freq.load(Ordering::Relaxed).into();
+
+ match self.src {
+ c if c == MainPllClkSrc::ClkIn || c == MainPllClkSrc::FFRO || c == MainPllClkSrc::SFRO => {
+ let mut base_rate;
+ match c {
+ MainPllClkSrc::ClkIn => {
+ clkctl0.syspll0clksel().write(|w| w.sel().sysxtal_clk());
+ let r = self.get_clock_rate()?;
+ base_rate = r;
+ }
+ MainPllClkSrc::FFRO => {
+ delay_loop_clocks(1000, desired_freq);
+ match clkctl0.ffroctl0().read().trim_range().is_ffro_48mhz() {
+ true => base_rate = Into::into(FfroFreq::Ffro48m),
+ false => base_rate = Into::into(FfroFreq::Ffro60m),
+ }
+ if div == 2 {
+ clkctl0.syspll0clksel().write(|w| w.sel().ffro_div_2());
+ delay_loop_clocks(150, desired_freq);
+ base_rate /= 2;
+ } else {
+ return Err(ClockError::InvalidDiv);
+ }
+ }
+ MainPllClkSrc::SFRO => {
+ base_rate = SFRO_FREQ;
+ clkctl0.syspll0clksel().write(|w| w.sel().sfro_clk());
+ }
+ };
+ base_rate *= u32::from(mult);
+ if base_rate != freq {
+ // make sure to power syspll back up before returning the error
+ // Clear System PLL reset
+ clkctl0.syspll0ctl0().write(|w| w.reset().normal());
+ // Power up SYSPLL
+ sysctl0
+ .pdruncfg0_clr()
+ .write(|w| w.syspllana_pd().clr_pdruncfg0().syspllldo_pd().clr_pdruncfg0());
+ return Err(ClockError::InvalidFrequency);
+ }
+ // SAFETY: unsafe needed to write the bits for the num and demon fields
+ clkctl0.syspll0num().write(|w| unsafe { w.num().bits(0b0) });
+ clkctl0.syspll0denom().write(|w| unsafe { w.denom().bits(0b1) });
+ delay_loop_clocks(30, desired_freq);
+ self.mult.store(mult, Ordering::Relaxed);
+ match mult {
+ 16 => {
+ clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_16());
+ }
+ 17 => {
+ clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_17());
+ }
+ 20 => {
+ clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_20());
+ }
+ 22 => {
+ clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_22());
+ }
+ 27 => {
+ clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_27());
+ }
+ 33 => {
+ clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_33());
+ }
+ _ => return Err(ClockError::InvalidMult),
+ }
+ // Clear System PLL reset
+ clkctl0.syspll0ctl0().modify(|_r, w| w.reset().normal());
+ // Power up SYSPLL
+ sysctl0
+ .pdruncfg0_clr()
+ .write(|w| w.syspllana_pd().clr_pdruncfg0().syspllldo_pd().clr_pdruncfg0());
+
+ // Set System PLL HOLDRINGOFF_ENA
+ clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().enable());
+ delay_loop_clocks(75, desired_freq);
+
+ // Clear System PLL HOLDRINGOFF_ENA
+ clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().dsiable());
+ delay_loop_clocks(15, desired_freq);
+
+ // gate the output and clear bits.
+ // SAFETY: unsafe needed to write the bits for pfd0
+ clkctl0
+ .syspll0pfd()
+ .modify(|_, w| unsafe { w.pfd0_clkgate().gated().pfd0().bits(0x0) });
+ // set pfd bits and un-gate the clock output
+ // output is multiplied by syspll * 18/pfd0_bits
+ // SAFETY: unsafe needed to write the bits for pfd0
+ clkctl0
+ .syspll0pfd()
+ .modify(|_r, w| unsafe { w.pfd0_clkgate().not_gated().pfd0().bits(0x12) });
+ // wait for ready bit to be set
+ delay_loop_clocks(50, desired_freq);
+ while clkctl0.syspll0pfd().read().pfd0_clkrdy().bit_is_clear() {}
+ // clear by writing a 1
+ clkctl0.syspll0pfd().modify(|_, w| w.pfd0_clkrdy().set_bit());
+
+ Ok(())
+ }
+ _ => Err(ClockError::ClockNotSupported),
+ }
+ } else {
+ Err(ClockError::ClockNotEnabled)
+ }
+ }
+ fn is_enabled(&self) -> bool {
+ self.state == State::Enabled
+ }
+}
+
+impl MainPllClkConfig {
+ /// Calculate the mult value of a desired frequency, return error if invalid
+ pub(self) fn calc_mult(rate: u32, base_freq: u32) -> Result {
+ const VALIDMULTS: [u8; 6] = [16, 17, 20, 22, 27, 33];
+ if rate > base_freq && rate % base_freq == 0 {
+ let mult = (rate / base_freq) as u8;
+ if VALIDMULTS.into_iter().any(|i| i == mult) {
+ Ok(mult)
+ } else {
+ Err(ClockError::InvalidFrequency)
+ }
+ } else {
+ Err(ClockError::InvalidFrequency)
+ }
+ }
+ pub(self) fn init_syspll() {
+ // SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0
+ let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };
+ let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };
+
+ // Power down SYSPLL before change fractional settings
+ sysctl0
+ .pdruncfg0_set()
+ .write(|w| w.syspllldo_pd().set_pdruncfg0().syspllana_pd().set_pdruncfg0());
+
+ clkctl0.syspll0clksel().write(|w| w.sel().ffro_div_2());
+ // SAFETY: unsafe needed to write the bits for both num and denom
+ clkctl0.syspll0num().write(|w| unsafe { w.num().bits(0x0) });
+ clkctl0.syspll0denom().write(|w| unsafe { w.denom().bits(0x1) });
+
+ // kCLOCK_SysPllMult22
+ clkctl0.syspll0ctl0().modify(|_, w| w.mult().div_22());
+
+ // Clear System PLL reset
+ clkctl0.syspll0ctl0().modify(|_, w| w.reset().normal());
+
+ // Power up SYSPLL
+ sysctl0
+ .pdruncfg0_clr()
+ .write(|w| w.syspllldo_pd().clr_pdruncfg0().syspllana_pd().clr_pdruncfg0());
+ delay_loop_clocks((150 & 0xFFFF) / 2, 12_000_000);
+
+ // Set System PLL HOLDRINGOFF_ENA
+ clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().enable());
+ delay_loop_clocks((150 & 0xFFFF) / 2, 12_000_000);
+
+ // Clear System PLL HOLDRINGOFF_ENA
+ clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().dsiable());
+ delay_loop_clocks((15 & 0xFFFF) / 2, 12_000_000);
+ }
+ /// enables default settings for pfd2 bits
+ pub(self) fn init_syspll_pfd2(config_bits: u8) {
+ // SAFETY: unsafe needed to take pointer to Clkctl0 and write specific bits
+ // needed to change the output of pfd0
+ unsafe {
+ let clkctl0 = crate::pac::Clkctl0::steal();
+
+ // Disable the clock output first.
+ // SAFETY: unsafe needed to write the bits for pfd2
+ clkctl0
+ .syspll0pfd()
+ .modify(|_, w| w.pfd2_clkgate().gated().pfd2().bits(0x0));
+
+ // Set the new value and enable output.
+ // SAFETY: unsafe needed to write the bits for pfd2
+ clkctl0
+ .syspll0pfd()
+ .modify(|_, w| w.pfd2_clkgate().not_gated().pfd2().bits(config_bits));
+
+ // Wait for output becomes stable.
+ while clkctl0.syspll0pfd().read().pfd2_clkrdy().bit_is_clear() {}
+
+ // Clear ready status flag.
+ clkctl0.syspll0pfd().modify(|_, w| w.pfd2_clkrdy().clear_bit());
+ }
+ }
+ /// Enables default settings for pfd0
+ pub(self) fn init_syspll_pfd0(config_bits: u8) {
+ // SAFETY: unsafe needed to take pointer to Clkctl0 and write specific bits
+ // needed to change the output of pfd0
+ unsafe {
+ let clkctl0 = crate::pac::Clkctl0::steal();
+ // Disable the clock output first
+ clkctl0
+ .syspll0pfd()
+ .modify(|_, w| w.pfd0_clkgate().gated().pfd0().bits(0x0));
+
+ // Set the new value and enable output
+ clkctl0
+ .syspll0pfd()
+ .modify(|_, w| w.pfd0_clkgate().not_gated().pfd0().bits(config_bits));
+
+ // Wait for output becomes stable
+ while clkctl0.syspll0pfd().read().pfd0_clkrdy().bit_is_clear() {}
+
+ // Clear ready status flag
+ clkctl0.syspll0pfd().modify(|_, w| w.pfd0_clkrdy().clear_bit());
+ }
+ }
+}
+
+impl MainClkConfig {
+ fn init_main_clk() {
+ // SAFETY:: unsafe needed to take pointers to Clkctl0 and Clkctl1
+ // used to set the right HW frequency
+ let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };
+ let clkctl1 = unsafe { crate::pac::Clkctl1::steal() };
+
+ clkctl0.mainclkselb().write(|w| w.sel().main_pll_clk());
+
+ // Set PFC0DIV divider to value 2, Subtract 1 since 0-> 1, 1-> 2, etc...
+ clkctl0.pfcdiv(0).modify(|_, w| w.reset().set_bit());
+ // SAFETY: unsafe needed to write the bits for pfcdiv
+ clkctl0
+ .pfcdiv(0)
+ .write(|w| unsafe { w.div().bits(2 - 1).halt().clear_bit() });
+ while clkctl0.pfcdiv(0).read().reqflag().bit_is_set() {}
+
+ // Set FRGPLLCLKDIV divider to value 12, Subtract 1 since 0-> 1, 1-> 2, etc...
+ clkctl1.frgpllclkdiv().modify(|_, w| w.reset().set_bit());
+ // SAFETY: unsafe needed to write the bits for frgpllclkdiv
+ clkctl1
+ .frgpllclkdiv()
+ .write(|w| unsafe { w.div().bits(12 - 1).halt().clear_bit() });
+ while clkctl1.frgpllclkdiv().read().reqflag().bit_is_set() {}
+ }
+}
+impl MultiSourceClock for MainClkConfig {
+ fn get_clock_source_and_rate(&self, clock: &Clocks) -> Result<(Clocks, u32), ClockError> {
+ match clock {
+ Clocks::MainClk => {
+ let div: u32 = if self.src == MainClkSrc::FFROdiv4 { 4 } else { 1 };
+ let converted_clock = Clocks::from(self.src);
+ match ConfigurableClock::get_clock_rate(self) {
+ Ok(_rate) => {
+ // SAFETY: unsafe needed to take pointer to Clkctl0
+ // needed to calculate the clock rate from the bits written in the registers
+ let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };
+ if self.src == MainClkSrc::PllMain && clkctl0.syspll0ctl0().read().bypass().is_programmed_clk()
+ {
+ let mut temp;
+ temp = self.freq.load(Ordering::Relaxed)
+ * u32::from(clkctl0.syspll0ctl0().read().mult().bits());
+ temp = (u64::from(temp) * 18 / u64::from(clkctl0.syspll0pfd().read().pfd0().bits())) as u32;
+ return Ok((converted_clock, temp));
+ }
+ Ok((converted_clock, self.freq.load(Ordering::Relaxed) / div))
+ }
+ Err(clk_err) => Err(clk_err),
+ }
+ }
+ _ => Err(ClockError::ClockMismatch),
+ }
+ }
+ fn set_clock_source_and_rate(
+ &mut self,
+ clock_src_config: &mut impl ConfigurableClock,
+ clock_src: &Clocks,
+ rate: u32,
+ ) -> Result<(), ClockError> {
+ if !clock_src_config.is_enabled() {
+ return Err(ClockError::ClockNotEnabled);
+ }
+ if let Ok(c) = >::try_into(*clock_src) {
+ // SAFETY: unsafe needed to take pointer to Clkctl0
+ // needed to change the clock source
+ let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };
+ match c {
+ MainClkSrc::ClkIn => {
+ self.src = MainClkSrc::ClkIn;
+
+ clkctl0.mainclksela().write(|w| w.sel().sysxtal_clk());
+ clkctl0.mainclkselb().write(|w| w.sel().main_1st_clk());
+ Ok(())
+ }
+ // the following will yield the same result as if compared to FFROdiv4
+ MainClkSrc::FFRO | MainClkSrc::FFROdiv4 => match rate {
+ div4 if div4 == (FfroFreq::Ffro60m as u32) / 4 || div4 == (FfroFreq::Ffro48m as u32) / 4 => {
+ self.src = MainClkSrc::FFROdiv4;
+ self.freq.store(div4, Ordering::Relaxed);
+
+ clkctl0.mainclksela().write(|w| w.sel().ffro_div_4());
+ clkctl0.mainclkselb().write(|w| w.sel().main_1st_clk());
+ Ok(())
+ }
+ div1 if div1 == FfroFreq::Ffro60m as u32 || div1 == FfroFreq::Ffro48m as u32 => {
+ self.src = MainClkSrc::FFRO;
+ self.freq.store(div1, Ordering::Relaxed);
+
+ clkctl0.mainclksela().write(|w| w.sel().ffro_clk());
+ clkctl0.mainclkselb().write(|w| w.sel().main_1st_clk());
+ Ok(())
+ }
+ _ => Err(ClockError::InvalidFrequency),
+ },
+ MainClkSrc::Lposc => {
+ if let Ok(r) = >::try_into(rate) {
+ match r {
+ LposcFreq::Lp1m => {
+ self.src = MainClkSrc::Lposc;
+ self.freq.store(rate, Ordering::Relaxed);
+
+ clkctl0.mainclksela().write(|w| w.sel().lposc());
+ clkctl0.mainclkselb().write(|w| w.sel().main_1st_clk());
+ Ok(())
+ }
+ LposcFreq::Lp32k => Err(ClockError::InvalidFrequency),
+ }
+ } else {
+ Err(ClockError::InvalidFrequency)
+ }
+ }
+ MainClkSrc::SFRO => {
+ if rate == SFRO_FREQ {
+ self.src = MainClkSrc::SFRO;
+ self.freq.store(rate, Ordering::Relaxed);
+ clkctl0.mainclkselb().write(|w| w.sel().sfro_clk());
+ Ok(())
+ } else {
+ Err(ClockError::InvalidFrequency)
+ }
+ }
+ MainClkSrc::PllMain => {
+ let r = rate;
+ // From Section 4.6.1.1 Pll Limitations of the RT6xx User manual
+ let pll_max = 572_000_000;
+ let pll_min = 80_000_000;
+ if pll_min <= r && r <= pll_max {
+ clkctl0.mainclkselb().write(|w| w.sel().main_pll_clk());
+ self.src = MainClkSrc::PllMain;
+ self.freq.store(r, Ordering::Relaxed);
+ Ok(())
+ } else {
+ Err(ClockError::InvalidFrequency)
+ }
+ }
+ MainClkSrc::RTC32k => {
+ if rate == RtcFreq::SubSecond32kHz as u32 {
+ self.src = MainClkSrc::RTC32k;
+ self.freq.store(rate, Ordering::Relaxed);
+ clkctl0.mainclkselb().write(|w| w.sel().rtc_32k_clk());
+ Ok(())
+ } else {
+ Err(ClockError::InvalidFrequency)
+ }
+ }
+ }
+ } else {
+ Err(ClockError::ClockNotSupported)
+ }
+ }
+}
+
+impl ConfigurableClock for MainClkConfig {
+ fn enable_and_reset(&self) -> Result<(), ClockError> {
+ MainClkConfig::init_main_clk();
+ Ok(())
+ }
+ fn disable(&self) -> Result<(), ClockError> {
+ Err(ClockError::ClockNotSupported)
+ }
+ fn get_clock_rate(&self) -> Result {
+ let (_c, rate) = MainClkConfig::get_clock_source_and_rate(self, &Clocks::MainClk)?;
+ Ok(rate)
+ }
+ fn set_clock_rate(&mut self, _div: u8, _mult: u8, _freq: u32) -> Result<(), ClockError> {
+ Err(ClockError::ClockNotSupported)
+ }
+ fn is_enabled(&self) -> bool {
+ self.state == State::Enabled
+ }
+}
+
+impl ConfigurableClock for ClkInConfig {
+ fn enable_and_reset(&self) -> Result<(), ClockError> {
+ // External Input, no hw writes needed
+ Ok(())
+ }
+ fn disable(&self) -> Result<(), ClockError> {
+ error!("Attempting to reset a clock input");
+ Err(ClockError::ClockNotSupported)
+ }
+ fn get_clock_rate(&self) -> Result {
+ if self.freq.is_some() {
+ Ok(self.freq.as_ref().unwrap().load(Ordering::Relaxed))
+ } else {
+ Err(ClockError::ClockNotEnabled)
+ }
+ }
+ fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {
+ self.freq.as_ref().unwrap().store(freq, Ordering::Relaxed);
+ Ok(())
+ }
+ fn is_enabled(&self) -> bool {
+ self.state == State::Enabled
+ }
+}
+
+impl RtcClkConfig {
+ /// Register writes to initialize the RTC Clock
+ fn init_rtc_clk() {
+ // SAFETY: unsafe needed to take pointer to Clkctl0, Clkctl1, and RTC
+ // needed to enable the RTC HW
+ let cc0 = unsafe { pac::Clkctl0::steal() };
+ let cc1 = unsafe { pac::Clkctl1::steal() };
+ let r = unsafe { pac::Rtc::steal() };
+ // Enable the RTC peripheral clock
+ cc1.pscctl2_set().write(|w| w.rtc_lite_clk_set().set_clock());
+ // Make sure the reset bit is cleared amd RTC OSC is powered up
+ r.ctrl().modify(|_, w| w.swreset().not_in_reset().rtc_osc_pd().enable());
+
+ // set initial match value, note that with a 15 bit count-down timer this would
+ // typically be 0x8000, but we are "doing some clever things" in time-driver.rs,
+ // read more about it in the comments there
+ // SAFETY: unsafe needed to write the bits
+ r.wake().write(|w| unsafe { w.bits(0xA) });
+
+ // Enable 32K OSC
+ cc0.osc32khzctl0().write(|w| w.ena32khz().enabled());
+
+ // enable rtc clk
+ r.ctrl().modify(|_, w| w.rtc_en().enable());
+ }
+}
+
+impl ConfigurableClock for RtcClkConfig {
+ fn enable_and_reset(&self) -> Result<(), ClockError> {
+ // should only be called once if previously disabled
+ RtcClkConfig::init_rtc_clk();
+ Ok(())
+ }
+ fn disable(&self) -> Result<(), ClockError> {
+ Err(ClockError::ClockNotSupported)
+ }
+ fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {
+ if let Ok(r) = >::try_into(freq) {
+ // SAFETY: unsafe needed to take pointer to RTC
+ // needed to enable the HW for the different RTC frequencies, powered down by default
+ let rtc = unsafe { crate::pac::Rtc::steal() };
+ match r {
+ RtcFreq::Default1Hz => {
+ if rtc.ctrl().read().rtc_en().is_enable() {
+ } else {
+ rtc.ctrl().modify(|_r, w| w.rtc_en().enable());
+ }
+ Ok(())
+ }
+ RtcFreq::HighResolution1khz => {
+ if rtc.ctrl().read().rtc1khz_en().is_enable() {
+ } else {
+ rtc.ctrl().modify(|_r, w| w.rtc1khz_en().enable());
+ }
+ Ok(())
+ }
+ RtcFreq::SubSecond32kHz => {
+ if rtc.ctrl().read().rtc_subsec_ena().is_enable() {
+ } else {
+ rtc.ctrl().modify(|_r, w| w.rtc_subsec_ena().enable());
+ }
+ Ok(())
+ }
+ }
+ } else {
+ Err(ClockError::InvalidFrequency)
+ }
+ }
+ // unlike the others, since this provides multiple clocks, return the fastest one
+ fn get_clock_rate(&self) -> Result {
+ if self.sub_second_state == State::Enabled {
+ Ok(RtcFreq::SubSecond32kHz as u32)
+ } else if self.wake_alarm_state == State::Enabled {
+ Ok(RtcFreq::HighResolution1khz as u32)
+ } else if self.state == State::Enabled {
+ Ok(RtcFreq::Default1Hz as u32)
+ } else {
+ Err(ClockError::ClockNotEnabled)
+ }
+ }
+ fn is_enabled(&self) -> bool {
+ self.state == State::Enabled
+ }
+}
+
+impl SysClkConfig {
+ /// Updates the system core clock frequency, SW concept used for systick
+ fn update_sys_core_clock(&self) {}
+}
+
+impl ConfigurableClock for SysOscConfig {
+ fn enable_and_reset(&self) -> Result<(), ClockError> {
+ if self.state == State::Enabled {
+ return Ok(());
+ }
+
+ // SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0, needed to modify clock HW
+ let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };
+ let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };
+
+ // Let CPU run on ffro for safe switching
+ clkctl0.mainclksela().write(|w| w.sel().ffro_clk());
+ clkctl0.mainclksela().write(|w| w.sel().ffro_div_4());
+
+ // Power on SYSXTAL
+ sysctl0.pdruncfg0_clr().write(|w| w.sysxtal_pd().clr_pdruncfg0());
+
+ // Enable system OSC
+ clkctl0
+ .sysoscctl0()
+ .write(|w| w.lp_enable().lp().bypass_enable().normal_mode());
+
+ delay_loop_clocks(260, SYS_OSC_DEFAULT_FREQ.into());
+ Ok(())
+ }
+ fn disable(&self) -> Result<(), ClockError> {
+ // SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0, needed to modify clock HW
+ let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };
+ let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };
+
+ // Let CPU run on ffro for safe switching
+ clkctl0.mainclksela().write(|w| w.sel().ffro_clk());
+ clkctl0.mainclksela().write(|w| w.sel().ffro_div_4());
+
+ // Power on SYSXTAL
+ sysctl0.pdruncfg0_set().write(|w| w.sysxtal_pd().set_pdruncfg0());
+ Ok(())
+ }
+ fn get_clock_rate(&self) -> Result {
+ if self.state == State::Enabled {
+ Ok(SYS_OSC_DEFAULT_FREQ)
+ } else {
+ Err(ClockError::ClockNotEnabled)
+ }
+ }
+ fn is_enabled(&self) -> bool {
+ self.state == State::Enabled
+ }
+ fn set_clock_rate(&mut self, _div: u8, _mult: u8, _freq: u32) -> Result<(), ClockError> {
+ Err(ClockError::ClockNotSupported)
+ }
+}
+
+/// Method to delay for a certain number of microseconds given a clock rate
+pub fn delay_loop_clocks(usec: u64, freq_mhz: u64) {
+ let mut ticks = usec * freq_mhz / 1_000_000 / 4;
+ if ticks > u64::from(u32::MAX) {
+ ticks = u64::from(u32::MAX);
+ }
+ // won't panic since we check value above
+ cortex_m::asm::delay(ticks as u32);
+}
+
+/// Configure the pad voltage pmc registers for all 3 vddio ranges
+fn set_pad_voltage_range() {
+ // SAFETY: unsafe needed to take pointer to PNC as well as to write specific bits
+ unsafe {
+ let pmc = crate::pac::Pmc::steal();
+ // Set up IO voltages
+ // all 3 ranges need to be 1.71-1.98V which is 01
+ pmc.padvrange().write(|w| {
+ w.vddio_0range()
+ .bits(0b01)
+ .vddio_1range()
+ .bits(0b01)
+ .vddio_2range()
+ .bits(0b01)
+ });
+ }
+}
+
+/// Initialize AHB clock
+fn init_syscpuahb_clk() {
+ // SAFETY: unsafe needed to take pointer to Clkctl0
+ let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };
+ // SAFETY: unsafe needed to write the bits
+ // Set syscpuahbclkdiv to value 2, Subtract 1 since 0-> 1, 1-> 2, etc...
+ clkctl0.syscpuahbclkdiv().write(|w| unsafe { w.div().bits(2 - 1) });
+
+ while clkctl0.syscpuahbclkdiv().read().reqflag().bit_is_set() {}
+}
+
+/// `ClockOut` config
+pub struct ClockOutConfig {
+ src: ClkOutSrc,
+ div: u8,
+}
+
+/// `ClockOut` sources
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+/// `ClockOut` sources
+pub enum ClkOutSrc {
+ /// No Source, reduce power consumption
+ None,
+ /// SFRO clock
+ Sfro,
+ /// External input clock
+ ClkIn,
+ /// Low-power oscillator
+ Lposc,
+ /// FFRO clock
+ Ffro,
+ /// Main clock
+ MainClk,
+ /// Main DSP clock
+ DspMainClk,
+ /// Main Pll clock
+ MainPllClk,
+ /// `SysPll` Aux0 clock
+ Aux0PllClk,
+ /// `SysPll` DSP clock
+ DspPllClk,
+ /// `SysPll` Aux1 clock
+ Aux1PllClk,
+ /// Audio Pll clock
+ AudioPllClk,
+ /// 32 `KHz` RTC
+ RTC32k,
+}
+
+/// Initialize the `ClkOutConfig`
+impl ClockOutConfig {
+ /// Default configuration for Clock out
+ #[must_use]
+ pub fn default_config() -> Self {
+ Self {
+ src: ClkOutSrc::None,
+ div: 0,
+ }
+ }
+
+ /// Enable the Clock Out output
+ pub fn enable_and_reset(&mut self) -> Result<(), ClockError> {
+ self.set_clkout_source_and_div(self.src, self.div)?;
+ Ok(())
+ }
+
+ /// Disable Clock Out output and select None as the source to conserve power
+ pub fn disable(&mut self) -> Result<(), ClockError> {
+ self.set_clkout_source_and_div(ClkOutSrc::None, 0)?;
+ Ok(())
+ }
+
+ /// Set the source of the Clock Out pin
+ fn set_clkout_source(&mut self, src: ClkOutSrc) -> Result<(), ClockError> {
+ // SAFETY: unsafe needed to take pointers to Clkctl1, needed to set source in HW
+ let cc1 = unsafe { pac::Clkctl1::steal() };
+ match src {
+ ClkOutSrc::None => {
+ cc1.clkoutsel0().write(|w| w.sel().none());
+ cc1.clkoutsel1().write(|w| w.sel().none());
+ }
+ ClkOutSrc::Sfro => {
+ cc1.clkoutsel0().write(|w| w.sel().sfro_clk());
+ cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output());
+ }
+ ClkOutSrc::ClkIn => {
+ cc1.clkoutsel0().write(|w| w.sel().xtalin_clk());
+ cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output());
+ }
+ ClkOutSrc::Lposc => {
+ cc1.clkoutsel0().write(|w| w.sel().lposc());
+ cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output());
+ }
+ ClkOutSrc::Ffro => {
+ cc1.clkoutsel0().write(|w| w.sel().ffro_clk());
+ cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output());
+ }
+ ClkOutSrc::MainClk => {
+ cc1.clkoutsel0().write(|w| w.sel().main_clk());
+ cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output());
+ }
+ ClkOutSrc::DspMainClk => {
+ cc1.clkoutsel0().write(|w| w.sel().dsp_main_clk());
+ cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output());
+ }
+ ClkOutSrc::MainPllClk => {
+ cc1.clkoutsel0().write(|w| w.sel().none());
+ cc1.clkoutsel1().write(|w| w.sel().main_pll_clk());
+ }
+ ClkOutSrc::Aux0PllClk => {
+ cc1.clkoutsel0().write(|w| w.sel().none());
+ cc1.clkoutsel1().write(|w| w.sel().syspll0_aux0_pll_clk());
+ }
+ ClkOutSrc::DspPllClk => {
+ cc1.clkoutsel0().write(|w| w.sel().none());
+ cc1.clkoutsel1().write(|w| w.sel().dsp_pll_clk());
+ }
+ ClkOutSrc::AudioPllClk => {
+ cc1.clkoutsel0().write(|w| w.sel().none());
+ cc1.clkoutsel1().write(|w| w.sel().audio_pll_clk());
+ }
+ ClkOutSrc::Aux1PllClk => {
+ cc1.clkoutsel0().write(|w| w.sel().none());
+ cc1.clkoutsel1().write(|w| w.sel().syspll0_aux1_pll_clk());
+ }
+ ClkOutSrc::RTC32k => {
+ cc1.clkoutsel0().write(|w| w.sel().none());
+ cc1.clkoutsel1().write(|w| w.sel().rtc_clk_32khz());
+ }
+ }
+ self.src = src;
+ Ok(())
+ }
+ /// set the clock out divider
+ /// note that 1 will be added to div when mapping to the divider
+ /// so bits(0) -> divide by 1
+ /// ...
+ /// bits(255)-> divide by 256
+ pub fn set_clkout_divider(&self, div: u8) -> Result<(), ClockError> {
+ // don't wait for clock to be ready if there's no source
+ if self.src != ClkOutSrc::None {
+ let cc1 = unsafe { pac::Clkctl1::steal() };
+
+ cc1.clkoutdiv()
+ .modify(|_, w| unsafe { w.div().bits(div).halt().clear_bit() });
+ while cc1.clkoutdiv().read().reqflag().bit_is_set() {}
+ }
+ Ok(())
+ }
+ /// set the source and divider for the clockout pin
+ pub fn set_clkout_source_and_div(&mut self, src: ClkOutSrc, div: u8) -> Result<(), ClockError> {
+ self.set_clkout_source(src)?;
+
+ self.set_clkout_divider(div)?;
+
+ Ok(())
+ }
+}
+
+/// Using the config, enables all desired clocks to desired clock rates
+fn init_clock_hw(config: ClockConfig) -> Result<(), ClockError> {
+ if let Err(e) = config.rtc.enable_and_reset() {
+ return Err(e);
+ }
+
+ if let Err(e) = config.lposc.enable_and_reset() {
+ return Err(e);
+ }
+
+ if let Err(e) = config.ffro.enable_and_reset() {
+ return Err(e);
+ }
+
+ if let Err(e) = config.sfro.enable_and_reset() {
+ return Err(e);
+ }
+
+ if let Err(e) = config.sys_osc.enable_and_reset() {
+ return Err(e);
+ }
+
+ if let Err(e) = config.main_pll_clk.enable_and_reset() {
+ return Err(e);
+ }
+
+ // Move FLEXSPI clock source from main clock to FFRO to avoid instruction/data fetch issue in XIP when
+ // updating PLL and main clock.
+ // SAFETY: unsafe needed to take pointers to Clkctl0
+ let cc0 = unsafe { pac::Clkctl0::steal() };
+ cc0.flexspifclksel().write(|w| w.sel().ffro_clk());
+
+ // Move ESPI clock source to FFRO
+ #[cfg(feature = "_espi")]
+ {
+ cc0.espiclksel().write(|w| w.sel().use_48_60m());
+ }
+
+ init_syscpuahb_clk();
+
+ if let Err(e) = config.main_clk.enable_and_reset() {
+ return Err(e);
+ }
+
+ config.sys_clk.update_sys_core_clock();
+ Ok(())
+}
+
+/// SAFETY: must be called exactly once at bootup
+pub(crate) unsafe fn init(config: ClockConfig) -> Result<(), ClockError> {
+ init_clock_hw(config)?;
+
+ // set VDDIO ranges 0-2
+ set_pad_voltage_range();
+ Ok(())
+}
+
+///Trait to expose perph clocks
+trait SealedSysconPeripheral {
+ fn enable_perph_clock();
+ fn reset_perph();
+ fn disable_perph_clock();
+}
+
+/// Clock and Reset control for peripherals
+#[allow(private_bounds)]
+pub trait SysconPeripheral: SealedSysconPeripheral + 'static {}
+/// Enables and resets peripheral `T`.
+///
+/// # Safety
+///
+/// Peripheral must not be in use.
+pub fn enable_and_reset() {
+ T::enable_perph_clock();
+ T::reset_perph();
+}
+
+/// Enables peripheral `T`.
+pub fn enable() {
+ T::enable_perph_clock();
+}
+
+/// Reset peripheral `T`.
+pub fn reset() {
+ T::reset_perph();
+}
+
+/// Disables peripheral `T`.
+///
+/// # Safety
+///
+/// Peripheral must not be in use.
+pub fn disable() {
+ T::disable_perph_clock();
+}
+macro_rules! impl_perph_clk {
+ ($peripheral:ident, $clkctl:ident, $clkreg:ident, $rstctl:ident, $rstreg:ident, $bit:expr) => {
+ impl SealedSysconPeripheral for crate::peripherals::$peripheral {
+ fn enable_perph_clock() {
+ // SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1
+ let cc1 = unsafe { pac::$clkctl::steal() };
+
+ paste! {
+ // SAFETY: unsafe due to the use of bits()
+ cc1.[<$clkreg _set>]().write(|w| unsafe { w.bits(1 << $bit) });
+ }
+ }
+
+ fn reset_perph() {
+ // SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1
+ let rc1 = unsafe { pac::$rstctl::steal() };
+
+ paste! {
+ // SAFETY: unsafe due to the use of bits()
+ rc1.[<$rstreg _clr>]().write(|w| unsafe { w.bits(1 << $bit) });
+ }
+ }
+
+ fn disable_perph_clock() {
+ // SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1
+ let cc1 = unsafe { pac::$clkctl::steal() };
+
+ paste! {
+ // SAFETY: unsafe due to the use of bits()
+ cc1.[<$clkreg _clr>]().write(|w| unsafe { w.bits(1 << $bit) });
+ }
+ }
+ }
+
+ impl SysconPeripheral for crate::peripherals::$peripheral {}
+ };
+}
+
+// These should enabled once the relevant peripherals are implemented.
+// impl_perph_clk!(GPIOINTCTL, Clkctl1, pscctl2, Rstctl1, prstctl2, 30);
+// impl_perph_clk!(OTP, Clkctl0, pscctl0, Rstctl0, prstctl0, 17);
+
+// impl_perph_clk!(ROM_CTL_128KB, Clkctl0, pscctl0, Rstctl0, prstctl0, 2);
+// impl_perph_clk!(USBHS_SRAM, Clkctl0, pscctl0, Rstctl0, prstctl0, 23);
+
+impl_perph_clk!(PIMCTL, Clkctl1, pscctl2, Rstctl1, prstctl2, 31);
+impl_perph_clk!(ACMP, Clkctl0, pscctl1, Rstctl0, prstctl1, 15);
+impl_perph_clk!(ADC0, Clkctl0, pscctl1, Rstctl0, prstctl1, 16);
+impl_perph_clk!(CASPER, Clkctl0, pscctl0, Rstctl0, prstctl0, 9);
+impl_perph_clk!(CRC, Clkctl1, pscctl1, Rstctl1, prstctl1, 16);
+impl_perph_clk!(CTIMER0_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 0);
+impl_perph_clk!(CTIMER1_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 1);
+impl_perph_clk!(CTIMER2_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 2);
+impl_perph_clk!(CTIMER3_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 3);
+impl_perph_clk!(CTIMER4_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 4);
+impl_perph_clk!(DMA0, Clkctl1, pscctl1, Rstctl1, prstctl1, 23);
+impl_perph_clk!(DMA1, Clkctl1, pscctl1, Rstctl1, prstctl1, 24);
+impl_perph_clk!(DMIC0, Clkctl1, pscctl0, Rstctl1, prstctl0, 24);
+
+#[cfg(feature = "_espi")]
+impl_perph_clk!(ESPI, Clkctl0, pscctl1, Rstctl0, prstctl1, 7);
+
+impl_perph_clk!(FLEXCOMM0, Clkctl1, pscctl0, Rstctl1, prstctl0, 8);
+impl_perph_clk!(FLEXCOMM1, Clkctl1, pscctl0, Rstctl1, prstctl0, 9);
+impl_perph_clk!(FLEXCOMM14, Clkctl1, pscctl0, Rstctl1, prstctl0, 22);
+impl_perph_clk!(FLEXCOMM15, Clkctl1, pscctl0, Rstctl1, prstctl0, 23);
+impl_perph_clk!(FLEXCOMM2, Clkctl1, pscctl0, Rstctl1, prstctl0, 10);
+impl_perph_clk!(FLEXCOMM3, Clkctl1, pscctl0, Rstctl1, prstctl0, 11);
+impl_perph_clk!(FLEXCOMM4, Clkctl1, pscctl0, Rstctl1, prstctl0, 12);
+impl_perph_clk!(FLEXCOMM5, Clkctl1, pscctl0, Rstctl1, prstctl0, 13);
+impl_perph_clk!(FLEXCOMM6, Clkctl1, pscctl0, Rstctl1, prstctl0, 14);
+impl_perph_clk!(FLEXCOMM7, Clkctl1, pscctl0, Rstctl1, prstctl0, 15);
+impl_perph_clk!(FLEXSPI, Clkctl0, pscctl0, Rstctl0, prstctl0, 16);
+impl_perph_clk!(FREQME, Clkctl1, pscctl1, Rstctl1, prstctl1, 31);
+impl_perph_clk!(HASHCRYPT, Clkctl0, pscctl0, Rstctl0, prstctl0, 10);
+impl_perph_clk!(HSGPIO0, Clkctl1, pscctl1, Rstctl1, prstctl1, 0);
+impl_perph_clk!(HSGPIO1, Clkctl1, pscctl1, Rstctl1, prstctl1, 1);
+impl_perph_clk!(HSGPIO2, Clkctl1, pscctl1, Rstctl1, prstctl1, 2);
+impl_perph_clk!(HSGPIO3, Clkctl1, pscctl1, Rstctl1, prstctl1, 3);
+impl_perph_clk!(HSGPIO4, Clkctl1, pscctl1, Rstctl1, prstctl1, 4);
+impl_perph_clk!(HSGPIO5, Clkctl1, pscctl1, Rstctl1, prstctl1, 5);
+impl_perph_clk!(HSGPIO6, Clkctl1, pscctl1, Rstctl1, prstctl1, 6);
+impl_perph_clk!(HSGPIO7, Clkctl1, pscctl1, Rstctl1, prstctl1, 7);
+impl_perph_clk!(I3C0, Clkctl1, pscctl2, Rstctl1, prstctl2, 16);
+impl_perph_clk!(MRT0, Clkctl1, pscctl2, Rstctl1, prstctl2, 8);
+impl_perph_clk!(MU_A, Clkctl1, pscctl1, Rstctl1, prstctl1, 28);
+impl_perph_clk!(OS_EVENT, Clkctl1, pscctl0, Rstctl1, prstctl0, 27);
+impl_perph_clk!(POWERQUAD, Clkctl0, pscctl0, Rstctl0, prstctl0, 8);
+impl_perph_clk!(PUF, Clkctl0, pscctl0, Rstctl0, prstctl0, 11);
+impl_perph_clk!(RNG, Clkctl0, pscctl0, Rstctl0, prstctl0, 12);
+impl_perph_clk!(RTC, Clkctl1, pscctl2, Rstctl1, prstctl2, 7);
+impl_perph_clk!(SCT0, Clkctl0, pscctl0, Rstctl0, prstctl0, 24);
+impl_perph_clk!(SECGPIO, Clkctl0, pscctl1, Rstctl0, prstctl1, 24);
+impl_perph_clk!(SEMA42, Clkctl1, pscctl1, Rstctl1, prstctl1, 29);
+impl_perph_clk!(USBHSD, Clkctl0, pscctl0, Rstctl0, prstctl0, 21);
+impl_perph_clk!(USBHSH, Clkctl0, pscctl0, Rstctl0, prstctl0, 22);
+impl_perph_clk!(USBPHY, Clkctl0, pscctl0, Rstctl0, prstctl0, 20);
+impl_perph_clk!(USDHC0, Clkctl0, pscctl1, Rstctl0, prstctl1, 2);
+impl_perph_clk!(USDHC1, Clkctl0, pscctl1, Rstctl0, prstctl1, 3);
+impl_perph_clk!(UTICK0, Clkctl0, pscctl2, Rstctl0, prstctl2, 0);
+impl_perph_clk!(WDT0, Clkctl0, pscctl2, Rstctl0, prstctl2, 1);
+impl_perph_clk!(WDT1, Clkctl1, pscctl2, Rstctl1, prstctl2, 10);
diff --git a/embassy-imxrt/src/crc.rs b/embassy-imxrt/src/crc.rs
new file mode 100644
index 000000000..24d6ba5bd
--- /dev/null
+++ b/embassy-imxrt/src/crc.rs
@@ -0,0 +1,190 @@
+//! Cyclic Redundancy Check (CRC)
+
+use core::marker::PhantomData;
+
+use crate::clocks::{enable_and_reset, SysconPeripheral};
+pub use crate::pac::crc_engine::mode::CrcPolynomial as Polynomial;
+use crate::{peripherals, Peri, PeripheralType};
+
+/// CRC driver.
+pub struct Crc<'d> {
+ info: Info,
+ _config: Config,
+ _lifetime: PhantomData<&'d ()>,
+}
+
+/// CRC configuration
+pub struct Config {
+ /// Polynomial to be used
+ pub polynomial: Polynomial,
+
+ /// Reverse bit order of input?
+ pub reverse_in: bool,
+
+ /// 1's complement input?
+ pub complement_in: bool,
+
+ /// Reverse CRC bit order?
+ pub reverse_out: bool,
+
+ /// 1's complement CRC?
+ pub complement_out: bool,
+
+ /// CRC Seed
+ pub seed: u32,
+}
+
+impl Config {
+ /// Create a new CRC config.
+ #[must_use]
+ pub fn new(
+ polynomial: Polynomial,
+ reverse_in: bool,
+ complement_in: bool,
+ reverse_out: bool,
+ complement_out: bool,
+ seed: u32,
+ ) -> Self {
+ Config {
+ polynomial,
+ reverse_in,
+ complement_in,
+ reverse_out,
+ complement_out,
+ seed,
+ }
+ }
+}
+
+impl Default for Config {
+ fn default() -> Self {
+ Self {
+ polynomial: Polynomial::CrcCcitt,
+ reverse_in: false,
+ complement_in: false,
+ reverse_out: false,
+ complement_out: false,
+ seed: 0xffff,
+ }
+ }
+}
+
+impl<'d> Crc<'d> {
+ /// Instantiates new CRC peripheral and initializes to default values.
+ pub fn new(_peripheral: Peri<'d, T>, config: Config) -> Self {
+ // enable CRC clock
+ enable_and_reset::();
+
+ let mut instance = Self {
+ info: T::info(),
+ _config: config,
+ _lifetime: PhantomData,
+ };
+
+ instance.reconfigure();
+ instance
+ }
+
+ /// Reconfigured the CRC peripheral.
+ fn reconfigure(&mut self) {
+ self.info.regs.mode().write(|w| {
+ w.crc_poly()
+ .variant(self._config.polynomial)
+ .bit_rvs_wr()
+ .variant(self._config.reverse_in)
+ .cmpl_wr()
+ .variant(self._config.complement_in)
+ .bit_rvs_sum()
+ .variant(self._config.reverse_out)
+ .cmpl_sum()
+ .variant(self._config.complement_out)
+ });
+
+ // Init CRC value
+ self.info
+ .regs
+ .seed()
+ .write(|w| unsafe { w.crc_seed().bits(self._config.seed) });
+ }
+
+ /// Feeds a byte into the CRC peripheral. Returns the computed checksum.
+ pub fn feed_byte(&mut self, byte: u8) -> u32 {
+ self.info.regs.wr_data8().write(|w| unsafe { w.bits(byte) });
+
+ self.info.regs.sum().read().bits()
+ }
+
+ /// Feeds an slice of bytes into the CRC peripheral. Returns the computed checksum.
+ pub fn feed_bytes(&mut self, bytes: &[u8]) -> u32 {
+ let (prefix, data, suffix) = unsafe { bytes.align_to::() };
+
+ for b in prefix {
+ self.info.regs.wr_data8().write(|w| unsafe { w.bits(*b) });
+ }
+
+ for d in data {
+ self.info.regs.wr_data32().write(|w| unsafe { w.bits(*d) });
+ }
+
+ for b in suffix {
+ self.info.regs.wr_data8().write(|w| unsafe { w.bits(*b) });
+ }
+
+ self.info.regs.sum().read().bits()
+ }
+
+ /// Feeds a halfword into the CRC peripheral. Returns the computed checksum.
+ pub fn feed_halfword(&mut self, halfword: u16) -> u32 {
+ self.info.regs.wr_data16().write(|w| unsafe { w.bits(halfword) });
+
+ self.info.regs.sum().read().bits()
+ }
+
+ /// Feeds an slice of halfwords into the CRC peripheral. Returns the computed checksum.
+ pub fn feed_halfwords(&mut self, halfwords: &[u16]) -> u32 {
+ for halfword in halfwords {
+ self.info.regs.wr_data16().write(|w| unsafe { w.bits(*halfword) });
+ }
+
+ self.info.regs.sum().read().bits()
+ }
+
+ /// Feeds a words into the CRC peripheral. Returns the computed checksum.
+ pub fn feed_word(&mut self, word: u32) -> u32 {
+ self.info.regs.wr_data32().write(|w| unsafe { w.bits(word) });
+
+ self.info.regs.sum().read().bits()
+ }
+
+ /// Feeds an slice of words into the CRC peripheral. Returns the computed checksum.
+ pub fn feed_words(&mut self, words: &[u32]) -> u32 {
+ for word in words {
+ self.info.regs.wr_data32().write(|w| unsafe { w.bits(*word) });
+ }
+
+ self.info.regs.sum().read().bits()
+ }
+}
+
+struct Info {
+ regs: crate::pac::CrcEngine,
+}
+
+trait SealedInstance {
+ fn info() -> Info;
+}
+
+/// CRC instance trait.
+#[allow(private_bounds)]
+pub trait Instance: SealedInstance + PeripheralType + SysconPeripheral + 'static + Send {}
+
+impl Instance for peripherals::CRC {}
+
+impl SealedInstance for peripherals::CRC {
+ fn info() -> Info {
+ // SAFETY: safe from single executor
+ Info {
+ regs: unsafe { crate::pac::CrcEngine::steal() },
+ }
+ }
+}
diff --git a/embassy-imxrt/src/dma.rs b/embassy-imxrt/src/dma.rs
new file mode 100644
index 000000000..e141447f3
--- /dev/null
+++ b/embassy-imxrt/src/dma.rs
@@ -0,0 +1,418 @@
+//! DMA driver.
+
+use core::future::Future;
+use core::pin::Pin;
+use core::sync::atomic::{compiler_fence, Ordering};
+use core::task::{Context, Poll};
+
+use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType};
+use embassy_sync::waitqueue::AtomicWaker;
+use pac::dma0::channel::cfg::Periphreqen;
+use pac::dma0::channel::xfercfg::{Dstinc, Srcinc, Width};
+
+use crate::clocks::enable_and_reset;
+use crate::interrupt::InterruptExt;
+use crate::peripherals::DMA0;
+use crate::sealed::Sealed;
+use crate::{interrupt, pac, peripherals, BitIter};
+
+#[cfg(feature = "rt")]
+#[interrupt]
+fn DMA0() {
+ let reg = unsafe { crate::pac::Dma0::steal() };
+
+ if reg.intstat().read().activeerrint().bit() {
+ let err = reg.errint0().read().bits();
+
+ for channel in BitIter(err) {
+ error!("DMA error interrupt on channel {}!", channel);
+ reg.errint0().write(|w| unsafe { w.err().bits(1 << channel) });
+ CHANNEL_WAKERS[channel as usize].wake();
+ }
+ }
+
+ if reg.intstat().read().activeint().bit() {
+ let ia = reg.inta0().read().bits();
+
+ for channel in BitIter(ia) {
+ reg.inta0().write(|w| unsafe { w.ia().bits(1 << channel) });
+ CHANNEL_WAKERS[channel as usize].wake();
+ }
+ }
+}
+
+/// Initialize DMA controllers (DMA0 only, for now)
+pub(crate) unsafe fn init() {
+ let sysctl0 = crate::pac::Sysctl0::steal();
+ let dmactl0 = crate::pac::Dma0::steal();
+
+ enable_and_reset::();
+
+ interrupt::DMA0.disable();
+ interrupt::DMA0.set_priority(interrupt::Priority::P3);
+
+ dmactl0.ctrl().modify(|_, w| w.enable().set_bit());
+
+ // Set channel descriptor SRAM base address
+ // Descriptor base must be 1K aligned
+ let descriptor_base = core::ptr::addr_of!(DESCRIPTORS.descs) as u32;
+ dmactl0.srambase().write(|w| w.bits(descriptor_base));
+
+ // Ensure AHB priority it highest (M4 == DMAC0)
+ sysctl0.ahbmatrixprior().modify(|_, w| w.m4().bits(0));
+
+ interrupt::DMA0.unpend();
+ interrupt::DMA0.enable();
+}
+
+/// DMA read.
+///
+/// SAFETY: Slice must point to a valid location reachable by DMA.
+pub unsafe fn read<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: *const W, to: *mut [W]) -> Transfer<'a, C> {
+ let count = ((to.len() / W::size() as usize) - 1) as isize;
+
+ copy_inner(
+ ch,
+ from as *const u32,
+ (to as *mut u32).byte_offset(count * W::size()),
+ W::width(),
+ count,
+ false,
+ true,
+ true,
+ )
+}
+
+/// DMA write.
+///
+/// SAFETY: Slice must point to a valid location reachable by DMA.
+pub unsafe fn write<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: *const [W], to: *mut W) -> Transfer<'a, C> {
+ let count = ((from.len() / W::size() as usize) - 1) as isize;
+
+ copy_inner(
+ ch,
+ (from as *const u32).byte_offset(count * W::size()),
+ to as *mut u32,
+ W::width(),
+ count,
+ true,
+ false,
+ true,
+ )
+}
+
+/// DMA copy between slices.
+///
+/// SAFETY: Slices must point to locations reachable by DMA.
+pub unsafe fn copy<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: &[W], to: &mut [W]) -> Transfer<'a, C> {
+ let from_len = from.len();
+ let to_len = to.len();
+ assert_eq!(from_len, to_len);
+
+ let count = ((from_len / W::size() as usize) - 1) as isize;
+
+ copy_inner(
+ ch,
+ from.as_ptr().byte_offset(count * W::size()) as *const u32,
+ to.as_mut_ptr().byte_offset(count * W::size()) as *mut u32,
+ W::width(),
+ count,
+ true,
+ true,
+ false,
+ )
+}
+
+fn copy_inner<'a, C: Channel>(
+ ch: Peri<'a, C>,
+ from: *const u32,
+ to: *mut u32,
+ width: Width,
+ count: isize,
+ incr_read: bool,
+ incr_write: bool,
+ periph: bool,
+) -> Transfer<'a, C> {
+ let p = ch.regs();
+
+ unsafe {
+ DESCRIPTORS.descs[ch.number() as usize].src = from as u32;
+ DESCRIPTORS.descs[ch.number() as usize].dest = to as u32;
+ }
+
+ compiler_fence(Ordering::SeqCst);
+
+ p.errint0().write(|w| unsafe { w.err().bits(1 << ch.number()) });
+ p.inta0().write(|w| unsafe { w.ia().bits(1 << ch.number()) });
+
+ p.channel(ch.number().into()).cfg().write(|w| {
+ unsafe { w.chpriority().bits(0) }
+ .periphreqen()
+ .variant(match periph {
+ false => Periphreqen::Disabled,
+ true => Periphreqen::Enabled,
+ })
+ .hwtrigen()
+ .clear_bit()
+ });
+
+ p.intenset0().write(|w| unsafe { w.inten().bits(1 << ch.number()) });
+
+ p.channel(ch.number().into()).xfercfg().write(|w| {
+ unsafe { w.xfercount().bits(count as u16) }
+ .cfgvalid()
+ .set_bit()
+ .clrtrig()
+ .set_bit()
+ .reload()
+ .clear_bit()
+ .setinta()
+ .set_bit()
+ .width()
+ .variant(width)
+ .srcinc()
+ .variant(match incr_read {
+ false => Srcinc::NoIncrement,
+ true => Srcinc::WidthX1,
+ // REVISIT: what about WidthX2 and WidthX4?
+ })
+ .dstinc()
+ .variant(match incr_write {
+ false => Dstinc::NoIncrement,
+ true => Dstinc::WidthX1,
+ // REVISIT: what about WidthX2 and WidthX4?
+ })
+ });
+
+ p.enableset0().write(|w| unsafe { w.ena().bits(1 << ch.number()) });
+
+ p.channel(ch.number().into())
+ .xfercfg()
+ .modify(|_, w| w.swtrig().set_bit());
+
+ compiler_fence(Ordering::SeqCst);
+
+ Transfer::new(ch)
+}
+
+/// DMA transfer driver.
+#[must_use = "futures do nothing unless you `.await` or poll them"]
+pub struct Transfer<'a, C: Channel> {
+ channel: Peri<'a, C>,
+}
+
+impl<'a, C: Channel> Transfer<'a, C> {
+ pub(crate) fn new(channel: Peri<'a, C>) -> Self {
+ Self { channel }
+ }
+
+ pub(crate) fn abort(&mut self) -> usize {
+ let p = self.channel.regs();
+
+ p.abort0().write(|w| w.channel(self.channel.number()).set_bit());
+ while p.busy0().read().bsy().bits() & (1 << self.channel.number()) != 0 {}
+
+ p.enableclr0()
+ .write(|w| unsafe { w.clr().bits(1 << self.channel.number()) });
+
+ let width: u8 = p
+ .channel(self.channel.number().into())
+ .xfercfg()
+ .read()
+ .width()
+ .variant()
+ .unwrap()
+ .into();
+
+ let count = p
+ .channel(self.channel.number().into())
+ .xfercfg()
+ .read()
+ .xfercount()
+ .bits()
+ + 1;
+
+ usize::from(count) * usize::from(width)
+ }
+}
+
+impl<'a, C: Channel> Drop for Transfer<'a, C> {
+ fn drop(&mut self) {
+ self.abort();
+ }
+}
+
+impl<'a, C: Channel> Unpin for Transfer<'a, C> {}
+impl<'a, C: Channel> Future for Transfer<'a, C> {
+ type Output = ();
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll {
+ // Re-register the waker on each call to poll() because any calls to
+ // wake will deregister the waker.
+ CHANNEL_WAKERS[self.channel.number() as usize].register(cx.waker());
+
+ if self.channel.regs().active0().read().act().bits() & (1 << self.channel.number()) == 0 {
+ Poll::Ready(())
+ } else {
+ Poll::Pending
+ }
+ }
+}
+
+/// DMA channel descriptor
+#[derive(Copy, Clone)]
+#[repr(C)]
+struct Descriptor {
+ reserved: u32,
+ src: u32,
+ dest: u32,
+ link: u32,
+}
+
+impl Descriptor {
+ const fn new() -> Self {
+ Self {
+ reserved: 0,
+ src: 0,
+ dest: 0,
+ link: 0,
+ }
+ }
+}
+
+#[repr(align(1024))]
+struct Descriptors {
+ descs: [Descriptor; CHANNEL_COUNT],
+}
+
+impl Descriptors {
+ const fn new() -> Self {
+ Self {
+ descs: [const { Descriptor::new() }; CHANNEL_COUNT],
+ }
+ }
+}
+
+static mut DESCRIPTORS: Descriptors = Descriptors::new();
+static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT];
+pub(crate) const CHANNEL_COUNT: usize = 33;
+
+/// DMA channel interface.
+#[allow(private_bounds)]
+pub trait Channel: PeripheralType + Sealed + Into + Sized + 'static {
+ /// Channel number.
+ fn number(&self) -> u8;
+
+ /// Channel registry block.
+ fn regs(&self) -> &'static pac::dma0::RegisterBlock {
+ unsafe { &*crate::pac::Dma0::ptr() }
+ }
+}
+
+/// DMA word.
+#[allow(private_bounds)]
+pub trait Word: Sealed {
+ /// Transfer width.
+ fn width() -> Width;
+
+ /// Size in bytes for the width.
+ fn size() -> isize;
+}
+
+impl Sealed for u8 {}
+impl Word for u8 {
+ fn width() -> Width {
+ Width::Bit8
+ }
+
+ fn size() -> isize {
+ 1
+ }
+}
+
+impl Sealed for u16 {}
+impl Word for u16 {
+ fn width() -> Width {
+ Width::Bit16
+ }
+
+ fn size() -> isize {
+ 2
+ }
+}
+
+impl Sealed for u32 {}
+impl Word for u32 {
+ fn width() -> Width {
+ Width::Bit32
+ }
+
+ fn size() -> isize {
+ 4
+ }
+}
+
+/// Type erased DMA channel.
+pub struct AnyChannel {
+ number: u8,
+}
+
+impl_peripheral!(AnyChannel);
+
+impl Sealed for AnyChannel {}
+impl Channel for AnyChannel {
+ fn number(&self) -> u8 {
+ self.number
+ }
+}
+
+macro_rules! channel {
+ ($name:ident, $num:expr) => {
+ impl Sealed for peripherals::$name {}
+ impl Channel for peripherals::$name {
+ fn number(&self) -> u8 {
+ $num
+ }
+ }
+
+ impl From for crate::dma::AnyChannel {
+ fn from(val: peripherals::$name) -> Self {
+ Self { number: val.number() }
+ }
+ }
+ };
+}
+
+channel!(DMA0_CH0, 0);
+channel!(DMA0_CH1, 1);
+channel!(DMA0_CH2, 2);
+channel!(DMA0_CH3, 3);
+channel!(DMA0_CH4, 4);
+channel!(DMA0_CH5, 5);
+channel!(DMA0_CH6, 6);
+channel!(DMA0_CH7, 7);
+channel!(DMA0_CH8, 8);
+channel!(DMA0_CH9, 9);
+channel!(DMA0_CH10, 10);
+channel!(DMA0_CH11, 11);
+channel!(DMA0_CH12, 12);
+channel!(DMA0_CH13, 13);
+channel!(DMA0_CH14, 14);
+channel!(DMA0_CH15, 15);
+channel!(DMA0_CH16, 16);
+channel!(DMA0_CH17, 17);
+channel!(DMA0_CH18, 18);
+channel!(DMA0_CH19, 19);
+channel!(DMA0_CH20, 20);
+channel!(DMA0_CH21, 21);
+channel!(DMA0_CH22, 22);
+channel!(DMA0_CH23, 23);
+channel!(DMA0_CH24, 24);
+channel!(DMA0_CH25, 25);
+channel!(DMA0_CH26, 26);
+channel!(DMA0_CH27, 27);
+channel!(DMA0_CH28, 28);
+channel!(DMA0_CH29, 29);
+channel!(DMA0_CH30, 30);
+channel!(DMA0_CH31, 31);
+channel!(DMA0_CH32, 32);
diff --git a/embassy-imxrt/src/flexcomm/mod.rs b/embassy-imxrt/src/flexcomm/mod.rs
new file mode 100644
index 000000000..4473c9a77
--- /dev/null
+++ b/embassy-imxrt/src/flexcomm/mod.rs
@@ -0,0 +1,252 @@
+//! Implements Flexcomm interface wrapper for easier usage across modules
+
+pub mod uart;
+
+use paste::paste;
+
+use crate::clocks::{enable_and_reset, SysconPeripheral};
+use crate::peripherals::{
+ FLEXCOMM0, FLEXCOMM1, FLEXCOMM14, FLEXCOMM15, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7,
+};
+use crate::{pac, PeripheralType};
+
+/// clock selection option
+#[derive(Copy, Clone, Debug)]
+pub enum Clock {
+ /// SFRO
+ Sfro,
+
+ /// FFRO
+ Ffro,
+
+ /// `AUDIO_PLL`
+ AudioPll,
+
+ /// MASTER
+ Master,
+
+ /// FCn_FRG with Main clock source
+ FcnFrgMain,
+
+ /// FCn_FRG with Pll clock source
+ FcnFrgPll,
+
+ /// FCn_FRG with Sfro clock source
+ FcnFrgSfro,
+
+ /// FCn_FRG with Ffro clock source
+ FcnFrgFfro,
+
+ /// disabled
+ None,
+}
+
+/// do not allow implementation of trait outside this mod
+mod sealed {
+ /// trait does not get re-exported outside flexcomm mod, allowing us to safely expose only desired APIs
+ pub trait Sealed {}
+}
+
+/// primary low-level flexcomm interface
+pub(crate) trait FlexcommLowLevel: sealed::Sealed + PeripheralType + SysconPeripheral + 'static + Send {
+ // fetch the flexcomm register block for direct manipulation
+ fn reg() -> &'static pac::flexcomm0::RegisterBlock;
+
+ // set the clock select for this flexcomm instance and remove from reset
+ fn enable(clk: Clock);
+}
+
+macro_rules! impl_flexcomm {
+ ($($idx:expr),*) => {
+ $(
+ paste!{
+ impl sealed::Sealed for crate::peripherals::[] {}
+
+ impl FlexcommLowLevel for crate::peripherals::[] {
+ fn reg() -> &'static crate::pac::flexcomm0::RegisterBlock {
+ // SAFETY: safe from single executor, enforce
+ // via peripheral reference lifetime tracking
+ unsafe {
+ &*crate::pac::[]::ptr()
+ }
+ }
+
+ fn enable(clk: Clock) {
+ // SAFETY: safe from single executor
+ let clkctl1 = unsafe { crate::pac::Clkctl1::steal() };
+
+ clkctl1.flexcomm($idx).fcfclksel().write(|w| match clk {
+ Clock::Sfro => w.sel().sfro_clk(),
+ Clock::Ffro => w.sel().ffro_clk(),
+ Clock::AudioPll => w.sel().audio_pll_clk(),
+ Clock::Master => w.sel().master_clk(),
+ Clock::FcnFrgMain => w.sel().fcn_frg_clk(),
+ Clock::FcnFrgPll => w.sel().fcn_frg_clk(),
+ Clock::FcnFrgSfro => w.sel().fcn_frg_clk(),
+ Clock::FcnFrgFfro => w.sel().fcn_frg_clk(),
+ Clock::None => w.sel().none(), // no clock? throw an error?
+ });
+
+ clkctl1.flexcomm($idx).frgclksel().write(|w| match clk {
+ Clock::FcnFrgMain => w.sel().main_clk(),
+ Clock::FcnFrgPll => w.sel().frg_pll_clk(),
+ Clock::FcnFrgSfro => w.sel().sfro_clk(),
+ Clock::FcnFrgFfro => w.sel().ffro_clk(),
+ _ => w.sel().none(), // not using frg ...
+ });
+
+ // todo: add support for frg div/mult
+ clkctl1
+ .flexcomm($idx)
+ .frgctl()
+ .write(|w|
+ // SAFETY: unsafe only used for .bits() call
+ unsafe { w.mult().bits(0) });
+
+ enable_and_reset::<[]>();
+ }
+ }
+ }
+ )*
+ }
+}
+
+impl_flexcomm!(0, 1, 2, 3, 4, 5, 6, 7);
+
+// TODO: FLEXCOMM 14 is untested. Enable SPI support on FLEXCOMM14
+// Add special case FLEXCOMM14
+impl sealed::Sealed for crate::peripherals::FLEXCOMM14 {}
+
+impl FlexcommLowLevel for crate::peripherals::FLEXCOMM14 {
+ fn reg() -> &'static crate::pac::flexcomm0::RegisterBlock {
+ // SAFETY: safe from single executor, enforce
+ // via peripheral reference lifetime tracking
+ unsafe { &*crate::pac::Flexcomm14::ptr() }
+ }
+
+ fn enable(clk: Clock) {
+ // SAFETY: safe from single executor
+ let clkctl1 = unsafe { crate::pac::Clkctl1::steal() };
+
+ clkctl1.fc14fclksel().write(|w| match clk {
+ Clock::Sfro => w.sel().sfro_clk(),
+ Clock::Ffro => w.sel().ffro_clk(),
+ Clock::AudioPll => w.sel().audio_pll_clk(),
+ Clock::Master => w.sel().master_clk(),
+ Clock::FcnFrgMain => w.sel().fcn_frg_clk(),
+ Clock::FcnFrgPll => w.sel().fcn_frg_clk(),
+ Clock::FcnFrgSfro => w.sel().fcn_frg_clk(),
+ Clock::FcnFrgFfro => w.sel().fcn_frg_clk(),
+ Clock::None => w.sel().none(), // no clock? throw an error?
+ });
+
+ clkctl1.frg14clksel().write(|w| match clk {
+ Clock::FcnFrgMain => w.sel().main_clk(),
+ Clock::FcnFrgPll => w.sel().frg_pll_clk(),
+ Clock::FcnFrgSfro => w.sel().sfro_clk(),
+ Clock::FcnFrgFfro => w.sel().ffro_clk(),
+ _ => w.sel().none(), // not using frg ...
+ });
+
+ // todo: add support for frg div/mult
+ clkctl1.frg14ctl().write(|w|
+ // SAFETY: unsafe only used for .bits() call
+ unsafe { w.mult().bits(0) });
+
+ enable_and_reset::();
+ }
+}
+
+// Add special case FLEXCOMM15
+impl sealed::Sealed for crate::peripherals::FLEXCOMM15 {}
+
+impl FlexcommLowLevel for crate::peripherals::FLEXCOMM15 {
+ fn reg() -> &'static crate::pac::flexcomm0::RegisterBlock {
+ // SAFETY: safe from single executor, enforce
+ // via peripheral reference lifetime tracking
+ unsafe { &*crate::pac::Flexcomm15::ptr() }
+ }
+
+ fn enable(clk: Clock) {
+ // SAFETY: safe from single executor
+ let clkctl1 = unsafe { crate::pac::Clkctl1::steal() };
+
+ clkctl1.fc15fclksel().write(|w| match clk {
+ Clock::Sfro => w.sel().sfro_clk(),
+ Clock::Ffro => w.sel().ffro_clk(),
+ Clock::AudioPll => w.sel().audio_pll_clk(),
+ Clock::Master => w.sel().master_clk(),
+ Clock::FcnFrgMain => w.sel().fcn_frg_clk(),
+ Clock::FcnFrgPll => w.sel().fcn_frg_clk(),
+ Clock::FcnFrgSfro => w.sel().fcn_frg_clk(),
+ Clock::FcnFrgFfro => w.sel().fcn_frg_clk(),
+ Clock::None => w.sel().none(), // no clock? throw an error?
+ });
+ clkctl1.frg15clksel().write(|w| match clk {
+ Clock::FcnFrgMain => w.sel().main_clk(),
+ Clock::FcnFrgPll => w.sel().frg_pll_clk(),
+ Clock::FcnFrgSfro => w.sel().sfro_clk(),
+ Clock::FcnFrgFfro => w.sel().ffro_clk(),
+ _ => w.sel().none(), // not using frg ...
+ });
+ // todo: add support for frg div/mult
+ clkctl1.frg15ctl().write(|w|
+ // SAFETY: unsafe only used for .bits() call
+ unsafe { w.mult().bits(0) });
+
+ enable_and_reset::();
+ }
+}
+
+macro_rules! into_mode {
+ ($mode:ident, $($fc:ident),*) => {
+ paste! {
+ /// Sealed Mode trait
+ trait []: FlexcommLowLevel {}
+
+ /// Select mode of operation
+ #[allow(private_bounds)]
+ pub trait []: [] {
+ /// Set mode of operation
+ fn []() {
+ Self::reg().pselid().write(|w| w.persel().[<$mode>]());
+ }
+ }
+ }
+
+ $(
+ paste!{
+ impl [] for crate::peripherals::$fc {}
+ impl [] for crate::peripherals::$fc {}
+ }
+ )*
+ }
+}
+
+into_mode!(usart, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7);
+into_mode!(spi, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, FLEXCOMM14);
+into_mode!(i2c, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, FLEXCOMM15);
+
+into_mode!(
+ i2s_transmit,
+ FLEXCOMM0,
+ FLEXCOMM1,
+ FLEXCOMM2,
+ FLEXCOMM3,
+ FLEXCOMM4,
+ FLEXCOMM5,
+ FLEXCOMM6,
+ FLEXCOMM7
+);
+
+into_mode!(
+ i2s_receive,
+ FLEXCOMM0,
+ FLEXCOMM1,
+ FLEXCOMM2,
+ FLEXCOMM3,
+ FLEXCOMM4,
+ FLEXCOMM5,
+ FLEXCOMM6,
+ FLEXCOMM7
+);
diff --git a/embassy-imxrt/src/flexcomm/uart.rs b/embassy-imxrt/src/flexcomm/uart.rs
new file mode 100644
index 000000000..230b30d43
--- /dev/null
+++ b/embassy-imxrt/src/flexcomm/uart.rs
@@ -0,0 +1,1230 @@
+//! Universal Asynchronous Receiver Transmitter (UART) driver.
+
+use core::future::poll_fn;
+use core::marker::PhantomData;
+use core::sync::atomic::{compiler_fence, AtomicU8, Ordering};
+use core::task::Poll;
+
+use embassy_futures::select::{select, Either};
+use embassy_hal_internal::drop::OnDrop;
+use embassy_hal_internal::{Peri, PeripheralType};
+use embassy_sync::waitqueue::AtomicWaker;
+use paste::paste;
+
+use crate::dma::AnyChannel;
+use crate::flexcomm::Clock;
+use crate::gpio::{AnyPin, GpioPin as Pin};
+use crate::interrupt::typelevel::Interrupt;
+use crate::iopctl::{DriveMode, DriveStrength, Inverter, IopctlPin, Pull, SlewRate};
+use crate::pac::usart0::cfg::{Clkpol, Datalen, Loop, Paritysel as Parity, Stoplen, Syncen, Syncmst};
+use crate::pac::usart0::ctl::Cc;
+use crate::sealed::Sealed;
+use crate::{dma, interrupt};
+
+/// Driver move trait.
+#[allow(private_bounds)]
+pub trait Mode: Sealed {}
+
+/// Blocking mode.
+pub struct Blocking;
+impl Sealed for Blocking {}
+impl Mode for Blocking {}
+
+/// Async mode.
+pub struct Async;
+impl Sealed for Async {}
+impl Mode for Async {}
+
+/// Uart driver.
+pub struct Uart<'a, M: Mode> {
+ tx: UartTx<'a, M>,
+ rx: UartRx<'a, M>,
+}
+
+/// Uart TX driver.
+pub struct UartTx<'a, M: Mode> {
+ info: Info,
+ tx_dma: Option>,
+ _phantom: PhantomData<(&'a (), M)>,
+}
+
+/// Uart RX driver.
+pub struct UartRx<'a, M: Mode> {
+ info: Info,
+ rx_dma: Option>,
+ _phantom: PhantomData<(&'a (), M)>,
+}
+
+/// UART config
+#[derive(Clone, Copy)]
+pub struct Config {
+ /// Baudrate of the Uart
+ pub baudrate: u32,
+ /// data length
+ pub data_bits: Datalen,
+ /// Parity
+ pub parity: Parity,
+ /// Stop bits
+ pub stop_bits: Stoplen,
+ /// Polarity of the clock
+ pub clock_polarity: Clkpol,
+ /// Sync/ Async operation selection
+ pub operation: Syncen,
+ /// Sync master/slave mode selection (only applicable in sync mode)
+ pub sync_mode_master_select: Syncmst,
+ /// USART continuous Clock generation enable in synchronous master mode.
+ pub continuous_clock: Cc,
+ /// Normal/ loopback mode
+ pub loopback_mode: Loop,
+ /// Clock type
+ pub clock: Clock,
+}
+
+impl Default for Config {
+ /// Default configuration for single channel sampling.
+ fn default() -> Self {
+ Self {
+ baudrate: 115_200,
+ data_bits: Datalen::Bit8,
+ parity: Parity::NoParity,
+ stop_bits: Stoplen::Bit1,
+ clock_polarity: Clkpol::FallingEdge,
+ operation: Syncen::AsynchronousMode,
+ sync_mode_master_select: Syncmst::Slave,
+ continuous_clock: Cc::ClockOnCharacter,
+ loopback_mode: Loop::Normal,
+ clock: crate::flexcomm::Clock::Sfro,
+ }
+ }
+}
+
+/// Uart Errors
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum Error {
+ /// Read error
+ Read,
+
+ /// Buffer overflow
+ Overrun,
+
+ /// Noise error
+ Noise,
+
+ /// Framing error
+ Framing,
+
+ /// Parity error
+ Parity,
+
+ /// Failure
+ Fail,
+
+ /// Invalid argument
+ InvalidArgument,
+
+ /// Uart baud rate cannot be supported with the given clock
+ UnsupportedBaudrate,
+
+ /// RX FIFO Empty
+ RxFifoEmpty,
+
+ /// TX FIFO Full
+ TxFifoFull,
+
+ /// TX Busy
+ TxBusy,
+}
+/// shorthand for -> `Result`
+pub type Result = core::result::Result;
+
+impl<'a, M: Mode> UartTx<'a, M> {
+ fn new_inner(tx_dma: Option>) -> Self {
+ let uarttx = Self {
+ info: T::info(),
+ tx_dma,
+ _phantom: PhantomData,
+ };
+ uarttx.info.refcnt.fetch_add(1, Ordering::Relaxed);
+ uarttx
+ }
+}
+
+impl<'a, M: Mode> Drop for UartTx<'a, M> {
+ fn drop(&mut self) {
+ if self.info.refcnt.fetch_sub(1, Ordering::Relaxed) == 1 {
+ while self.info.regs.stat().read().txidle().bit_is_clear() {}
+
+ self.info.regs.fifointenclr().modify(|_, w| {
+ w.txerr()
+ .set_bit()
+ .rxerr()
+ .set_bit()
+ .txlvl()
+ .set_bit()
+ .rxlvl()
+ .set_bit()
+ });
+
+ self.info
+ .regs
+ .fifocfg()
+ .modify(|_, w| w.dmatx().clear_bit().dmarx().clear_bit());
+
+ self.info.regs.cfg().modify(|_, w| w.enable().disabled());
+ }
+ }
+}
+
+impl<'a> UartTx<'a, Blocking> {
+ /// Create a new UART which can only send data
+ /// Unidirectional Uart - Tx only
+ pub fn new_blocking(_inner: Peri<'a, T>, tx: Peri<'a, impl TxPin>, config: Config) -> Result {
+ tx.as_tx();
+
+ let _tx = tx.into();
+ Uart::::init::(Some(_tx), None, None, None, config)?;
+
+ Ok(Self::new_inner::(None))
+ }
+
+ fn write_byte_internal(&mut self, byte: u8) -> Result<()> {
+ // SAFETY: unsafe only used for .bits()
+ self.info
+ .regs
+ .fifowr()
+ .write(|w| unsafe { w.txdata().bits(u16::from(byte)) });
+
+ Ok(())
+ }
+
+ fn blocking_write_byte(&mut self, byte: u8) -> Result<()> {
+ while self.info.regs.fifostat().read().txnotfull().bit_is_clear() {}
+
+ // Prevent the compiler from reordering write_byte_internal()
+ // before the loop above.
+ compiler_fence(Ordering::Release);
+
+ self.write_byte_internal(byte)
+ }
+
+ fn write_byte(&mut self, byte: u8) -> Result<()> {
+ if self.info.regs.fifostat().read().txnotfull().bit_is_clear() {
+ Err(Error::TxFifoFull)
+ } else {
+ self.write_byte_internal(byte)
+ }
+ }
+
+ /// Transmit the provided buffer blocking execution until done.
+ pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> {
+ for x in buf {
+ self.blocking_write_byte(*x)?;
+ }
+
+ Ok(())
+ }
+
+ /// Transmit the provided buffer. Non-blocking version, bails out
+ /// if it would block.
+ pub fn write(&mut self, buf: &[u8]) -> Result<()> {
+ for x in buf {
+ self.write_byte(*x)?;
+ }
+
+ Ok(())
+ }
+
+ /// Flush UART TX blocking execution until done.
+ pub fn blocking_flush(&mut self) -> Result<()> {
+ while self.info.regs.stat().read().txidle().bit_is_clear() {}
+ Ok(())
+ }
+
+ /// Flush UART TX.
+ pub fn flush(&mut self) -> Result<()> {
+ if self.info.regs.stat().read().txidle().bit_is_clear() {
+ Err(Error::TxBusy)
+ } else {
+ Ok(())
+ }
+ }
+}
+
+impl<'a, M: Mode> UartRx<'a, M> {
+ fn new_inner(rx_dma: Option>) -> Self {
+ let uartrx = Self {
+ info: T::info(),
+ rx_dma,
+ _phantom: PhantomData,
+ };
+ uartrx.info.refcnt.fetch_add(1, Ordering::Relaxed);
+ uartrx
+ }
+}
+
+impl<'a, M: Mode> Drop for UartRx<'a, M> {
+ fn drop(&mut self) {
+ if self.info.refcnt.fetch_sub(1, Ordering::Relaxed) == 1 {
+ while self.info.regs.stat().read().rxidle().bit_is_clear() {}
+
+ self.info.regs.fifointenclr().modify(|_, w| {
+ w.txerr()
+ .set_bit()
+ .rxerr()
+ .set_bit()
+ .txlvl()
+ .set_bit()
+ .rxlvl()
+ .set_bit()
+ });
+
+ self.info
+ .regs
+ .fifocfg()
+ .modify(|_, w| w.dmatx().clear_bit().dmarx().clear_bit());
+
+ self.info.regs.cfg().modify(|_, w| w.enable().disabled());
+ }
+ }
+}
+
+impl<'a> UartRx<'a, Blocking> {
+ /// Create a new blocking UART which can only receive data
+ pub fn new_blocking