consortium-hal-imx9 0.5.0

Consortium HAL for i.MX 9 family devices
Documentation
#![no_std]
// Copyright 2026 Ethan Wu
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

//! Consortium HAL for i.MX 9 family devices.
//!
//! Peripherals are owned, not addressed. [`init`] hands out the chip's
//! singletons once as a [`Peripherals`] struct, and each driver's constructor
//! consumes the one it drives:
//!
//! ```ignore
//! let p = consortium_hal_imx9::init();
//! let i2c = consortium_hal_imx9::i2c::I2c::new(p.LPI2C1, 24_000_000, 400_000);
//! ```
//!
//! Because the base address and NVIC line travel with the singleton, no driver
//! takes an MMIO pointer and no constructor is `unsafe`. Bring-up that runs
//! outside [`init`] — a second-stage loader, a test harness — can still reach a
//! peripheral through `peripherals::<NAME>::steal()`, which carries the
//! exclusivity obligation the safe path discharges.
//!
//! # Layering
//!
//! ```text
//! consortium-hal          Peri, PeripheralType, peripherals!, narrow traits
//!   └── consortium-hal-imx9  (this crate) drivers + generated singletons
//!         └── consortium-pac-imx9   register blocks, Interrupt, vector table
//! ```
//!
//! Both are re-exported ([`hal`], [`pac`]), so a firmware crate needs one dependency.
//! [`interrupts`], [`Interrupt`], and — behind `rt` — the `interrupt` attribute macro
//! come straight from the PAC.
//!
//! # Drivers
//!
//! Each is behind a feature, all on by default: `adc`, `can`, `dma`, `gpio`,
//! `i2c`, `i3c`, `lptmr`, `spi`, `timer`, `timer_driver`, `uart`, `watchdog`.
//!
//! `timer_driver` (`time-driver-lpit`) is the Embassy time driver over LPIT1;
//! `consortium-cfg` emits its bring-up into the generated `init()` unless
//! `[profile] time_driver = false`.
//!
//! # Chip selection
//!
//! `mimx93` or `mimx95` — **exactly one**. They select which generated singleton map
//! is mounted and are forwarded to the PAC so its register and interrupt definitions
//! match; enabling both is a duplicate-definition error.
//!
//! Default is `mimx95`. **A dependent selecting `mimx93` must set
//! `default-features = false`**, or `mimx95` comes along too and the PAC's `Interrupt`
//! enum becomes ambiguous. The doorbell and UART-transport crates all do this.
//!
//! # Interrupts are the application's
//!
//! This crate installs no `#[interrupt]` handlers. The application owns the vector
//! table and forwards each vector into the driver's entry point, parameterized with
//! the same singleton the driver consumed:
//!
//! ```rust,ignore
//! #[interrupt]
//! fn LPUART2() {
//!     consortium_hal_imx9::uart::on_buffered_interrupt::<peripherals::LPUART2>();
//! }
//! ```
//!
//! Enable `rt` so the `#[interrupt]` macro and PAC vector table are available there.
//!
//! # Generated code
//!
//! `src/peripherals/<chip>.rs` is generated by `scripts/pac/gen_peripherals.py` from
//! the same `consortium-data` chip dumps that feed `gen_interrupts.py`. It declares the
//! singletons and invokes each driver's `impl_<driver>!` macro with that instance's
//! base and NVIC line — so the generated file is **data only** and the driver module
//! owns the logic. Re-run the generator after changing a chip dump rather than editing
//! it.
//!
//! One wrinkle: i.MX9 eDMA channels have no dump entry, so the generator synthesizes
//! `<CONTROLLER>_CH<n>` singletons from the layout table in `dma.rs`.

#[cfg(test)]
extern crate std;

pub use consortium_hal as hal;
/// Ownership handle and marker trait for peripheral singletons, re-exported so
/// drivers in other crates can name them without depending on Embassy directly.
pub use consortium_hal::{Peri, PeripheralType};
pub use consortium_pac_imx9 as pac;

// Peripheral singletons and their driver bindings, generated from the chip
// databases by `scripts/pac/gen_peripherals.py`. Chip features are mutually
// exclusive: each mounts a different singleton map, so they cannot be unified.
// Diagnosed here so the error names the mistake rather than surfacing as a
// duplicate `mod chip`; the `not(mimx95)` below keeps this the only diagnostic.
#[cfg(all(feature = "mimx93", feature = "mimx95"))]
compile_error!(
    "chip features are mutually exclusive; enable exactly one of: `mimx93`, `mimx95`. \
     A dependency selecting the non-default chip must set `default-features = false`."
);

#[cfg(feature = "mimx95")]
#[path = "peripherals/imx95.rs"]
mod chip;
#[cfg(all(feature = "mimx93", not(feature = "mimx95")))]
#[path = "peripherals/imx93.rs"]
mod chip;

#[cfg(any(feature = "mimx93", feature = "mimx95"))]
pub use chip::{Peripherals, peripherals};

/// Take the chip's peripheral singletons.
///
/// # Panics
///
/// Panics if called more than once — the singletons are exactly that, and
/// handing them out twice would let two drivers claim the same registers.
#[cfg(any(feature = "mimx93", feature = "mimx95"))]
pub fn init() -> Peripherals {
    Peripherals::take()
}

/// Interrupt lines for the selected chip, re-exported from the PAC for direct
/// HAL consumers.
pub use pac::interrupts;
pub use pac::interrupts::Interrupt;
/// The `#[interrupt]` attribute macro, available when the `rt` feature is on.
#[cfg(feature = "rt")]
pub use pac::interrupts::interrupt;

pub mod clock;

#[cfg(feature = "adc")]
pub mod adc;

#[cfg(feature = "can")]
pub mod can;

#[cfg(feature = "dma")]
pub mod dma;

#[cfg(feature = "gpio")]
pub mod gpio;

#[cfg(feature = "i2c")]
pub mod i2c;

#[cfg(feature = "i3c")]
pub mod i3c;

#[cfg(feature = "timer")]
pub mod lptmr;

#[cfg(feature = "spi")]
pub mod spi;

#[cfg(feature = "time-driver-lpit")]
pub mod timer_driver;

#[cfg(feature = "timer")]
pub mod timer;

#[cfg(feature = "uart")]
pub mod uart;

#[cfg(feature = "watchdog")]
pub mod watchdog;