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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
//! Functions for reading and manipulating clock gates.
//!
//! Use these functions to acquire [`Locator`]s to peripheral clock
//! gates. Once you have a locator, use `set` to change the gate, and
//! `get` to read the gate.
//!
//! Some locator-returning functions require a constant `N`, representing
//! the Nth peripheral (the '4' in GPIO4). An invalid `N` for a given
//! peripheral generates a compile-time error.
//!
//! ```no_run
//! use imxrt_hal as hal;
//! use imxrt_ral as ral;
//!
//! use hal::ccm::clock_gate;
//!
//! # || -> Option<()> {
//! let mut ccm = unsafe { ral::ccm::CCM::instance() };
//! // OK: GPT1 is valid.
//! let setting = clock_gate::gpt_serial::<1>().get(&ccm);
//! # Some(()) }();
//! ```
//! ```compile_fail
//! # use imxrt_hal as hal;
//! # use imxrt_ral as ral;
//! # use hal::ccm::clock_gate;
//! # || -> Option<()> {
//! # let mut ccm = unsafe { ral::ccm::CCM::instance() };
//! // ERROR: GPT33 is NOT a real peripheral.
//! let setting = clock_gate::gpt_serial::<33>().get(&ccm);
//! # Some(()) }();
//! ```
//!
//! # Aggregate clock gates
//!
//! The module exposes collections of aggregate clock gates. It forms
//! collections based on the root clock. The ordering of clock gates
//! within these collections are unspecified.
//!
//! Additionally, the size of each collection may vary by chip. Since
//! the collections are `const`, their size is known at compile time.
//!
//! ```no_run
//! use imxrt_hal as hal;
//! use imxrt_ral as ral;
//!
//! use hal::ccm::clock_gate;
//!
//! # || -> Option<()> {
//! let mut ccm = unsafe { ral::ccm::CCM::instance() };
//!
//! // Turn on all clock gates downstream of the IPG clock.
//! clock_gate::IPG_CLOCK_GATES.iter().for_each(|clock_gate| {
//!     clock_gate.set(&mut ccm, clock_gate::ON);
//! });
//! # Some(()) }();
//! ```

use crate::ral::{self, ccm::CCM};

/// A clock gate setting.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum Setting {
    /// Clock is off in all modes.
    Off = 0,
    /// Clock is on in run mode, but off in WAIT and STOP modes.
    OnlyRun = 1,
    /// Clock is on in all modes, except stop mode.
    On = 3,
}

/// Helper constant to turn off clock gates.
pub const OFF: Setting = Setting::Off;
/// Helper constant to turn on clock gates.
pub const ON: Setting = Setting::On;

impl Setting {
    fn from_raw(raw: u32) -> Self {
        match raw {
            0 => Setting::Off,
            1 => Setting::OnlyRun,
            3 => Setting::On,
            _ => unreachable!(), // Reserved value in two bit field.
        }
    }
}

/// Clock gating register.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(unused)]
#[repr(u8)]
enum Register {
    CCGR0,
    CCGR1,
    CCGR2,
    CCGR3,
    CCGR4,
    CCGR5,
    CCGR6,
    CCGR7,
}

use Register::*;

/// Clock gate.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(unused)]
#[repr(u8)]
enum Gate {
    CG0,
    CG1,
    CG2,
    CG3,
    CG4,
    CG5,
    CG6,
    CG7,
    CG8,
    CG9,
    CG10,
    CG11,
    CG12,
    CG13,
    CG14,
    CG15,
}

use Gate::*;

impl Gate {
    /// Compute the bitshift required to access this gate field
    /// in the register.
    #[inline(always)]
    const fn shift(self) -> usize {
        self as usize * 2
    }
}

/// A clock gate locator.
///
/// These are reachable through the various function
/// provided in this module.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct Locator {
    register: Register,
    gate: Gate,
}

const fn locator(register: Register, gate: Gate) -> Locator {
    Locator { register, gate }
}

/// Acquire the clock gate register for a clock gate locator.
#[inline(always)]
fn clock_gate_register(ccm: &CCM, register: Register) -> &ral::RWRegister<u32> {
    match register {
        Register::CCGR0 => &ccm.CCGR0,
        Register::CCGR1 => &ccm.CCGR1,
        Register::CCGR2 => &ccm.CCGR2,
        Register::CCGR3 => &ccm.CCGR3,
        Register::CCGR4 => &ccm.CCGR4,
        Register::CCGR5 => &ccm.CCGR5,
        Register::CCGR6 => &ccm.CCGR6,
        Register::CCGR7 => {
            // If we're compiling this code, it's because a chip feature
            // is activated.
            cfg_if::cfg_if! {
                if #[cfg(any(chip = "imxrt1060", chip = "imxrt1064"))] {
                    // Only certain i.MX RT 10xx chips have CCGR7.
                    &ccm.CCGR7
                } else {
                    // The RAL's 'Valid' trait ensures that no clock gate locator
                    // with this clock gate register is reachable.
                    unreachable!()
                }
            }
        }
    }
}

impl Locator {
    /// Get the clock gate setting for the peripheral.
    pub fn get(&self, ccm: &CCM) -> Setting {
        let ccgr = clock_gate_register(ccm, self.register);
        let raw = (ccgr.read() >> self.gate.shift()) & 0b11;
        Setting::from_raw(raw)
    }

    /// Set the clock gate for this peripheral.
    pub fn set(&self, ccm: &mut CCM, setting: Setting) {
        let ccgr = clock_gate_register(ccm, self.register);
        let mut raw = ccgr.read();
        raw &= !(0b11 << self.gate.shift());
        raw |= (setting as u32) << self.gate.shift();
        ccgr.write(raw);
    }
}

/// Returns the PIT clock gate locator.
#[inline(always)]
pub const fn pit() -> Locator {
    locator(CCGR1, CG6)
}

/// Returns the GPT serial clock gate locator.
#[inline(always)]
pub const fn gpt_serial<const N: u8>() -> Locator
where
    ral::gpt::Instance<N>: ral::Valid,
{
    [locator(CCGR1, CG11), locator(CCGR0, CG13)][N as usize - 1]
}

/// Returns the GPT serial clock gate locator.
#[inline(always)]
pub const fn gpt_bus<const N: u8>() -> Locator
where
    ral::gpt::Instance<N>: ral::Valid,
{
    [locator(CCGR1, CG10), locator(CCGR0, CG12)][N as usize - 1]
}

/// Returns the GPIO clock gate locator.
///
/// Note that fast GPIOs do not have a clock gate,
/// so `N` should not describe a fast GPIO port.
///
/// # Panics
///
/// Panics if `N` describes a fast GPIO port.
#[inline(always)]
pub const fn gpio<const N: u8>() -> Locator
where
    ral::gpio::Instance<N>: ral::Valid,
{
    [
        locator(CCGR1, CG13),
        locator(CCGR0, CG15),
        locator(CCGR2, CG13),
        locator(CCGR3, CG6),
        locator(CCGR1, CG15),
    ][N as usize - 1]
}

/// Returns the LPUART clock gate locator.
#[inline(always)]
pub const fn lpuart<const N: u8>() -> Locator
where
    ral::lpuart::Instance<N>: ral::Valid,
{
    [
        locator(CCGR5, CG12),
        locator(CCGR0, CG14),
        locator(CCGR0, CG6),
        locator(CCGR1, CG12),
        locator(CCGR3, CG1),
        locator(CCGR3, CG3),
        locator(CCGR5, CG13),
        locator(CCGR6, CG7),
    ][N as usize - 1]
}

/// Returns the DMA clock gate locator.
#[inline(always)]
pub const fn dma() -> Locator {
    locator(CCGR5, CG3)
}

/// Returns the LPI2C clock gate locator.
#[inline(always)]
pub const fn lpi2c<const N: u8>() -> Locator
where
    ral::lpi2c::Instance<N>: ral::Valid,
{
    [
        locator(CCGR2, CG3),
        locator(CCGR2, CG4),
        locator(CCGR2, CG5),
        locator(CCGR6, CG12),
    ][N as usize - 1]
}

/// Returns the LPSPI clock gate locator.
#[inline(always)]
pub const fn lpspi<const N: u8>() -> Locator
where
    ral::lpspi::Instance<N>: ral::Valid,
{
    [
        locator(CCGR1, CG0),
        locator(CCGR1, CG1),
        locator(CCGR1, CG2),
        locator(CCGR1, CG3),
    ][N as usize - 1]
}

#[allow(clippy::assertions_on_constants)]
const _: () = assert!(ral::SOLE_INSTANCE == 0u8);

/// Returns the FlexIO clock gate locator.
#[inline(always)]
pub const fn flexio<const N: u8>() -> Locator
where
    ral::flexio::Instance<N>: ral::Valid,
{
    [
        locator(CCGR5, CG1),
        locator(CCGR3, CG0),
        locator(CCGR7, CG6),
    ][if N == ral::SOLE_INSTANCE {
        N as usize
    } else {
        N as usize - 1
    }]
}

/// Returns the FlexPWM clock gate locator.
#[inline(always)]
pub const fn flexpwm<const N: u8>() -> Locator
where
    ral::pwm::Instance<N>: ral::Valid,
{
    [
        locator(CCGR4, CG8),
        locator(CCGR4, CG9),
        locator(CCGR4, CG10),
        locator(CCGR4, CG11),
    ][if N == ral::SOLE_INSTANCE {
        N as usize
    } else {
        N as usize - 1
    }]
    // because 1010 has only one PWM instance, and that's
    // represented by ral::SOLE_INSTANCE (the number 0).
    // Consider this as the precedent approach when adding
    // support for 1170 (multiple PIT, DMA peripherals w.r.t
    // the 10xx families).
}

/// Returns the TRNG clock gate locator.
#[inline(always)]
pub const fn trng() -> Locator {
    locator(CCGR6, CG6)
}

/// Returns the SNVS LP clock gate locator.
#[inline(always)]
pub const fn snvs_lp() -> Locator {
    locator(CCGR5, CG15)
}

/// Returns the SNVS HP clock gate locator.
#[inline(always)]
pub const fn snvs_hp() -> Locator {
    locator(CCGR5, CG14)
}

/// Returns the USB clock gate locator.
#[inline(always)]
pub const fn usb() -> Locator {
    locator(CCGR6, CG0)
}

/// Returns the ADC clock gate locators.
#[inline(always)]
pub const fn adc<const N: u8>() -> Locator
where
    ral::adc::Instance<N>: ral::Valid,
{
    [locator(CCGR1, CG8), locator(CCGR1, CG4)][if N == ral::SOLE_INSTANCE {
        N as usize
    } else {
        N as usize - 1
    }]
}

pub use crate::chip::config::ccm::clock_gate::*;