1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// 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`.
//!
//! `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`.
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 ;
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; enabling several is a duplicate-definition error, matching the PAC.
pub use ;
/// 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.
/// Interrupt lines for the selected chip, re-exported from the PAC for direct
/// HAL consumers.
pub use interrupts;
pub use Interrupt;
/// The `#[interrupt]` attribute macro, available when the `rt` feature is on.
pub use interrupt;