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
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

use core::sync::atomic::{AtomicU32, Ordering};
use cortexm7::support::atomic;
use kernel::hil;
use kernel::hil::time::{Ticks, Ticks32, Time};
use kernel::platform::chip::ClockInterface;
use kernel::utilities::cells::OptionalCell;
use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
use kernel::utilities::registers::{register_bitfields, ReadOnly, ReadWrite};
use kernel::utilities::StaticRef;
use kernel::ErrorCode;

use crate::ccm;
use crate::nvic;

/// General purpose timers
#[repr(C)]
struct GptRegisters {
    /// GPT Control Register
    cr: ReadWrite<u32, CR::Register>,
    /// GPT Prescaler Register
    pr: ReadWrite<u32, PR::Register>,
    /// GPT Status Register
    sr: ReadWrite<u32, SR::Register>,
    /// GPT Interrupt Register
    ir: ReadWrite<u32, IR::Register>,
    /// GPT Output Compare Register 1
    ocr1: ReadWrite<u32, OCR1::Register>,
    /// GPT Output Compare Register 2
    ocr2: ReadWrite<u32, OCR2::Register>,
    /// GPT Output Compare Register 3
    ocr3: ReadWrite<u32, OCR3::Register>,
    /// GPT Input Capture Register 1
    icr1: ReadOnly<u32, ICR1::Register>,
    /// GPT Input Capture Register 2
    icr2: ReadOnly<u32, ICR2::Register>,
    /// GPT Counter Register
    cnt: ReadOnly<u32, CNT::Register>,
}

register_bitfields![u32,
    CR [
        /// Force Output Compare Channel 3
        FO3 OFFSET(31) NUMBITS(1) [],
        /// Force Output Compare Channel 2
        FO2 OFFSET(30) NUMBITS(1) [],
        /// Force Output Compare Channel 1
        FO1 OFFSET(29) NUMBITS(1) [],
        /// Controls the Output Compare Channel 3 operating mode
        OM3 OFFSET(26) NUMBITS(3) [],
        /// Controls the Output Compare Channel 2 operating mode
        OM2 OFFSET(23) NUMBITS(3) [],
        /// Controls the Output Compare Channel 2 operating mode
        OM1 OFFSET(20) NUMBITS(3) [],
        /// Input Capture Channel 2 operating mode
        IM2 OFFSET(18) NUMBITS(2) [],
        /// Input Capture Channel 1 operating mode
        IM1 OFFSET(16) NUMBITS(2) [],
        /// Software reset
        SWR OFFSET(15) NUMBITS(1) [],
        /// Enable 24 MHz clock input from crystal
        EN_24M OFFSET(10) NUMBITS(1) [],
        /// Free run or Restart mode
        FRR OFFSET(9) NUMBITS(1) [],
        /// Clock source select
        CLKSRC OFFSET(6) NUMBITS(3) [
            /// No clock
            NoClock = 0,
            /// Peripheral Clock (ipg_clk)
            PeripheralClock = 1,
            /// High Frequency Reference Clock (ipg_clk_highfreq)
            HighFrequencyReferenceClock = 2,
            /// External Clock
            ExternalClock = 3,
            /// Low Frequency Reference Clock (ipg_clk_32k)
            LowFrequencyReferenceClock = 4,
            /// Crystal oscillator as Reference Clock (ipg_clk_24M)
            CrystalOscillator = 5
        ],
        /// GPT Stop Mode enable
        STOPEN OFFSET(5) NUMBITS(1) [],
        /// GPT Doze Mode Enable
        DOZEEN OFFSET(4) NUMBITS(1) [],
        /// GPT Wait Mode enable
        WAITEN OFFSET(3) NUMBITS(1) [],
        /// GPT debug mode enable
        DBGEN OFFSET(2) NUMBITS(1) [],
        /// GPT Enable mode
        ENMOD OFFSET(1) NUMBITS(1) [],
        /// GPT Enable
        EN OFFSET(0) NUMBITS(1) []
    ],

    PR [
        /// Prescaler bits for 24M crystal clock
        PRESCALER24M OFFSET(12) NUMBITS(4),
        /// Prescaler bits
        PRESCALER OFFSET(0) NUMBITS(12)
    ],

    SR [
        /// Rollover Flag
        ROV OFFSET(5) NUMBITS(1),
        /// Input capture 2 Flag
        IF2 OFFSET(4) NUMBITS(1),
        /// Input capture 1 Flag
        IF1 OFFSET(3) NUMBITS(1),
        /// Output Compare 3 Flag
        OF3 OFFSET(2) NUMBITS(1),
        /// Output Compare 2 Flag
        OF2 OFFSET(1) NUMBITS(1),
        /// Output Compare 1 Flag
        OF1 OFFSET(0) NUMBITS(1)
    ],

    IR [
        /// Rollover Interrupt Enable
        ROVIE OFFSET(5) NUMBITS(1),
        /// Input capture 2 Interrupt Enable
        IF2IE OFFSET(4) NUMBITS(1),
        /// Input capture 1 Interrupt Enable
        IF1IE OFFSET(3) NUMBITS(1),
        /// Output Compare 3 Interrupt Enable
        OF3IE OFFSET(2) NUMBITS(1),
        /// Output Compare 2 Interrupt Enable
        OF2IE OFFSET(1) NUMBITS(1),
        /// Output Compare 1 Interrupt Enable
        OF1IE OFFSET(0) NUMBITS(1)
    ],

    OCR1 [
        COMP OFFSET(0) NUMBITS(32)
    ],

    OCR2 [
        COMP OFFSET(0) NUMBITS(32)
    ],

    OCR3 [
        COMP OFFSET(0) NUMBITS(32)
    ],

    ICR1 [
        CAPT OFFSET(0) NUMBITS(32)
    ],

    ICR2 [
        CAPT OFFSET(0) NUMBITS(32)
    ],

    CNT [
        COUNT OFFSET(0) NUMBITS(32)
    ]
];

const GPT1_BASE: StaticRef<GptRegisters> =
    unsafe { StaticRef::new(0x401EC000 as *const GptRegisters) };
const GPT2_BASE: StaticRef<GptRegisters> =
    unsafe { StaticRef::new(0x401F0000 as *const GptRegisters) };

pub struct Gpt<'a, S> {
    registers: StaticRef<GptRegisters>,
    clock: GptClock<'a>,
    client: OptionalCell<&'a dyn hil::time::AlarmClient>,
    irqn: u32,
    _selection: core::marker::PhantomData<S>,
}

pub type Gpt1<'a> = Gpt<'a, _1>;
pub type Gpt2<'a> = Gpt<'a, _2>;

impl<'a> Gpt1<'a> {
    pub const fn new_gpt1(ccm: &'a crate::ccm::Ccm) -> Self {
        Gpt::new(
            GPT1_BASE,
            nvic::GPT1,
            ccm::PeripheralClock::ccgr1(ccm, ccm::HCLK1::GPT1),
        )
    }
}

impl<'a> Gpt2<'a> {
    pub const fn new_gpt2(ccm: &'a crate::ccm::Ccm) -> Self {
        Gpt::new(
            GPT2_BASE,
            nvic::GPT2,
            ccm::PeripheralClock::ccgr0(ccm, ccm::HCLK0::GPT2),
        )
    }
}

impl<'a, S> Gpt<'a, S> {
    const fn new(
        registers: StaticRef<GptRegisters>,
        irqn: u32,
        clock_gate: ccm::PeripheralClock<'a>,
    ) -> Self {
        Gpt {
            registers,
            clock: GptClock(clock_gate),
            client: OptionalCell::empty(),
            irqn,
            _selection: core::marker::PhantomData,
        }
    }

    pub fn is_enabled_clock(&self) -> bool {
        self.clock.is_enabled()
    }

    pub fn enable_clock(&self) {
        self.clock.enable();
    }

    pub fn disable_clock(&self) {
        self.clock.disable();
    }

    pub fn handle_interrupt(&self) {
        self.registers.sr.modify(SR::OF1::SET);
        self.registers.ir.modify(IR::OF1IE::CLEAR);

        self.client.map(|client| client.alarm());
    }

    /// Start the GPT, specifying the peripheral clock selection and the peripheral clock divider
    ///
    /// If you select the crystal oscillator as the periodic clock root, the GPT will divide the
    /// input clock by 3.
    ///
    /// `divider` must be non-zero.
    pub fn start(&self, selection: ccm::PerclkClockSel, divider: u8) {
        // Disable GPT and the GPT interrupt register first
        self.registers.cr.modify(CR::EN::CLEAR);

        self.registers.ir.modify(IR::ROVIE::CLEAR);
        self.registers.ir.modify(IR::IF1IE::CLEAR);
        self.registers.ir.modify(IR::IF2IE::CLEAR);
        self.registers.ir.modify(IR::OF1IE::CLEAR);
        self.registers.ir.modify(IR::OF2IE::CLEAR);
        self.registers.ir.modify(IR::OF3IE::CLEAR);

        // Clear Output mode to disconnected
        self.registers.cr.modify(CR::OM1::CLEAR);
        self.registers.cr.modify(CR::OM2::CLEAR);
        self.registers.cr.modify(CR::OM3::CLEAR);

        // Disable Input Capture Mode
        self.registers.cr.modify(CR::IM1::CLEAR);
        self.registers.cr.modify(CR::IM2::CLEAR);

        // Reset all the registers to the their default values, except EN,
        // ENMOD, STOPEN, DOZEEN, WAITEN, and DBGEN bits in the CR
        self.registers.cr.modify(CR::SWR::SET);

        // wait until registers are cleared
        while self.registers.cr.is_set(CR::SWR) {}

        // Clear the GPT status register
        self.registers.sr.set(31_u32);

        // Enable free run mode
        self.registers.cr.modify(CR::FRR::SET);

        // Enable run in wait mode
        self.registers.cr.modify(CR::WAITEN::SET);

        // Enable run in stop mode
        self.registers.cr.modify(CR::STOPEN::SET);

        // Bring GPT counter to 0x00000000
        self.registers.cr.modify(CR::ENMOD::SET);

        // Set the value of the Output Compare Register
        self.registers.ocr1.set(0xFFFF_FFFF - 1);

        match selection {
            ccm::PerclkClockSel::IPG => {
                // Disable 24Mhz clock input from crystal
                self.registers.cr.modify(CR::EN_24M::CLEAR);

                // We will use the ipg_clk_highfreq provided by perclk_clk_root,
                // which runs at 24.75 MHz. Before calling set_alarm, we assume clock
                // to GPT1 has been enabled.
                self.registers.cr.modify(CR::CLKSRC.val(0x2_u32));

                // We do not prescale the value for the moment. We will do so
                // after we will set the ARM_PLL1 CLK accordingly.
                self.registers.pr.modify(PR::PRESCALER.val(0_u32));

                self.set_frequency(IMXRT1050_IPG_CLOCK_HZ / divider as u32);
            }
            ccm::PerclkClockSel::Oscillator => {
                // Enable 24MHz clock input
                self.registers
                    .cr
                    .modify(CR::EN_24M::SET + CR::CLKSRC::CrystalOscillator);

                // Funknown reasons, the 24HMz prescaler must be non-zero, even
                // though zero is a valid value according to the reference manual.
                // If it's not set, the counter doesn't count! Thanks to the se4L
                // project for adding a comment to their code.
                //
                // I'm also finding that it can't be too large; a prescaler of 8
                // for the 24MHz clock doesn't work!
                const DEFAULT_PRESCALER: u32 = 3;
                self.registers
                    .pr
                    .write(PR::PRESCALER24M.val(DEFAULT_PRESCALER - 1));
                self.set_frequency(OSCILLATOR_HZ / DEFAULT_PRESCALER / divider as u32);
            }
        }

        // Enable the GPT
        self.registers.cr.modify(CR::EN::SET);

        // Enable the Output Compare 1 Interrupt Enable
        self.registers.ir.modify(IR::OF1IE::SET);
    }

    fn set_frequency(&self, hz: u32) {
        let idx = match self.irqn {
            nvic::GPT1 => 0,
            nvic::GPT2 => 1,
            _ => unreachable!(),
        };
        GPT_FREQUENCIES[idx].store(hz, Ordering::Release);
    }
}

/// Assumed IPG clock frequency for the iMXRT1050 processor family.
///
/// TODO this is not a constant value; it changes when setting the ARM clock
/// frequency. Change this after correctly configuring ARM frequency.
const IMXRT1050_IPG_CLOCK_HZ: u32 = 24_750_000;
/// Crystal oscillator frequency
const OSCILLATOR_HZ: u32 = 24_000_000;

/// GPT selection tags
pub enum _1 {}
pub enum _2 {}

static GPT_FREQUENCIES: [AtomicU32; 2] = [AtomicU32::new(0), AtomicU32::new(0)];

impl hil::time::Frequency for _1 {
    fn frequency() -> u32 {
        GPT_FREQUENCIES[0].load(Ordering::Acquire)
    }
}

impl hil::time::Frequency for _2 {
    fn frequency() -> u32 {
        GPT_FREQUENCIES[1].load(Ordering::Acquire)
    }
}

impl<F: hil::time::Frequency> hil::time::Time for Gpt<'_, F> {
    type Frequency = F;
    type Ticks = Ticks32;

    fn now(&self) -> Ticks32 {
        Ticks32::from(self.registers.cnt.get())
    }
}

impl<'a, F: hil::time::Frequency> hil::time::Alarm<'a> for Gpt<'a, F> {
    fn set_alarm_client(&self, client: &'a dyn hil::time::AlarmClient) {
        self.client.set(client);
    }

    fn set_alarm(&self, reference: Self::Ticks, dt: Self::Ticks) {
        let mut expire = reference.wrapping_add(dt);
        let now = self.now();
        if !now.within_range(reference, expire) {
            expire = now;
        }

        if expire.wrapping_sub(now) < self.minimum_dt() {
            expire = now.wrapping_add(self.minimum_dt());
        }

        let _ = self.disarm();
        self.registers.ocr1.set(expire.into_u32());
        self.registers.ir.modify(IR::OF1IE::SET);
    }

    fn get_alarm(&self) -> Self::Ticks {
        Self::Ticks::from(self.registers.ocr1.get())
    }

    fn disarm(&self) -> Result<(), ErrorCode> {
        unsafe {
            atomic(|| {
                // Disable counter
                self.registers.ir.modify(IR::OF1IE::CLEAR);
                cortexm7::nvic::Nvic::new(self.irqn).clear_pending();
            });
        }
        Ok(())
    }

    fn is_armed(&self) -> bool {
        // If alarm is enabled, then OF1IE is set
        self.registers.ir.is_set(IR::OF1IE)
    }

    fn minimum_dt(&self) -> Self::Ticks {
        Self::Ticks::from(1)
    }
}

struct GptClock<'a>(ccm::PeripheralClock<'a>);

impl ClockInterface for GptClock<'_> {
    fn is_enabled(&self) -> bool {
        self.0.is_enabled()
    }

    fn enable(&self) {
        self.0.enable();
    }

    fn disable(&self) {
        self.0.disable();
    }
}