lpc55s6x/
ctimer0.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2025.
4
5//! Standard counter/timer (CTIMER) driver for the LPC55S6x family.
6//!
7//! Features supported:
8//! - 32‑bit timer counter with prescaler
9//! - Match registers with interrupt, reset, and stop control
10//! - Capture inputs for edge‑triggered timestamping
11//! - External match outputs and PWM mode
12//!
13//! Reference: *LPC55S6x/LPC55S2x/LPC552x User Manual* (NXP).
14
15use cortexm33::support::with_interrupts_disabled;
16use kernel::hil;
17use kernel::hil::time::{Alarm, Ticks, Ticks32, Time};
18use kernel::utilities::cells::{OptionalCell, VolatileCell};
19use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
20use kernel::utilities::registers::{register_bitfields, register_structs, ReadOnly, ReadWrite};
21use kernel::utilities::StaticRef;
22
23use crate::interrupts::CTIMER0;
24
25register_structs! {
26    /// Standard counter/timers (CTIMER0 to 4)
27    Ctimer0Registers {
28        /// Interrupt Register. The IR can be written to clear interrupts. The IR can be rea
29        (0x000 => ir: ReadWrite<u32, IR::Register>),
30        /// Timer Control Register. The TCR is used to control the Timer Counter functions.
31        (0x004 => tcr: ReadWrite<u32, TCR::Register>),
32        /// Timer Counter
33        (0x008 => tc: ReadWrite<u32>),
34        /// Prescale Register
35        (0x00C => pr: ReadWrite<u32, PR::Register>),
36        /// Prescale Counter
37        (0x010 => pc: ReadWrite<u32, PC::Register>),
38        /// Match Control Register
39        (0x014 => mcr: ReadWrite<u32, MCR::Register>),
40        /// Match Register . MR can be enabled through the MCR to reset the TC, stop both th
41        (0x018 => mr_0: ReadWrite<u32, MR0::Register>),
42        /// Match Register . MR can be enabled through the MCR to reset the TC, stop both th
43        (0x01C => mr_1: ReadWrite<u32>),
44        /// Match Register . MR can be enabled through the MCR to reset the TC, stop both th
45        (0x020 => mr_2: ReadWrite<u32>),
46        /// Match Register . MR can be enabled through the MCR to reset the TC, stop both th
47        (0x024 => mr_3: ReadWrite<u32>),
48        /// Capture Control Register. The CCR controls which edges of the capture inputs are
49        (0x028 => ccr: ReadWrite<u32, CCR::Register>),
50        /// Capture Register . CR is loaded with the value of TC when there is an event on t
51        (0x02C => cr_0: ReadOnly<u32>),
52        /// Capture Register . CR is loaded with the value of TC when there is an event on t
53        (0x030 => cr_1: ReadOnly<u32>),
54        /// Capture Register . CR is loaded with the value of TC when there is an event on t
55        (0x034 => cr_2: ReadOnly<u32>),
56        /// Capture Register . CR is loaded with the value of TC when there is an event on t
57        (0x038 => cr_3: ReadOnly<u32>),
58        /// External Match Register. The EMR controls the match function and the external ma
59        (0x03C => emr: ReadWrite<u32, EMR::Register>),
60        (0x040 => _reserved0),
61        /// Count Control Register. The CTCR selects between Timer and Counter mode, and in
62        (0x070 => ctcr: ReadWrite<u32, CTCR::Register>),
63        /// PWM Control Register. This register enables PWM mode for the external match pins
64        (0x074 => pwmc: ReadWrite<u32, PWMC::Register>),
65        /// Match Shadow Register
66        (0x078 => msr_0: ReadWrite<u32>),
67        /// Match Shadow Register
68        (0x07C => msr_1: ReadWrite<u32>),
69        /// Match Shadow Register
70        (0x080 => msr_2: ReadWrite<u32>),
71        /// Match Shadow Register
72        (0x084 => msr_3: ReadWrite<u32>),
73        (0x088 => @END),
74    }
75}
76register_bitfields![u32,
77IR [
78    MR0INT OFFSET(0) NUMBITS(1) [],
79    MR1INT OFFSET(1) NUMBITS(1) [],
80    MR2INT OFFSET(2) NUMBITS(1) [],
81    MR3INT OFFSET(3) NUMBITS(1) [],
82    CR0INT OFFSET(4) NUMBITS(1) [],
83    CR1INT OFFSET(5) NUMBITS(1) [],
84    CR2INT OFFSET(6) NUMBITS(1) [],
85    CR3INT OFFSET(7) NUMBITS(1) [],
86],
87TCR [
88    /// Counter enable.
89    CEN OFFSET(0) NUMBITS(1) [
90        /// Disabled.The counters are disabled.
91        DisabledTheCountersAreDisabled = 0,
92        /// Enabled. The Timer Counter and Prescale Counter are enabled.
93        EnabledTheTimerCounterAndPrescaleCounterAreEnabled = 1
94    ],
95    /// Counter reset.
96    CRST OFFSET(1) NUMBITS(1) [
97        /// Disabled. Do nothing.
98        DisabledDoNothing = 0,
99        /// Enabled. The Timer Counter and the Prescale Counter are synchronously reset on t
100        ENABLED = 1
101    ]
102],
103TC [
104    /// Timer counter value.
105    TCVAL OFFSET(0) NUMBITS(32) []
106],
107PR [
108    /// Prescale counter value.
109    PRVAL OFFSET(0) NUMBITS(32) []
110],
111PC [
112    /// Prescale counter value.
113    PCVAL OFFSET(0) NUMBITS(32) []
114],
115MCR [
116    /// Interrupt on MR0: an interrupt is generated when MR0 matches the value in the TC
117    MR0I OFFSET(0) NUMBITS(1) [],
118    /// Reset on MR0: the TC will be reset if MR0 matches it.
119    MR0R OFFSET(1) NUMBITS(1) [],
120    /// Stop on MR0: the TC and PC will be stopped and TCR[0] will be set to 0 if MR0 ma
121    MR0S OFFSET(2) NUMBITS(1) [],
122    /// Interrupt on MR1: an interrupt is generated when MR1 matches the value in the TC
123    MR1I OFFSET(3) NUMBITS(1) [],
124    /// Reset on MR1: the TC will be reset if MR1 matches it.
125    MR1R OFFSET(4) NUMBITS(1) [],
126    /// Stop on MR1: the TC and PC will be stopped and TCR[0] will be set to 0 if MR1 ma
127    MR1S OFFSET(5) NUMBITS(1) [],
128    /// Interrupt on MR2: an interrupt is generated when MR2 matches the value in the TC
129    MR2I OFFSET(6) NUMBITS(1) [],
130    /// Reset on MR2: the TC will be reset if MR2 matches it.
131    MR2R OFFSET(7) NUMBITS(1) [],
132    /// Stop on MR2: the TC and PC will be stopped and TCR[0] will be set to 0 if MR2 ma
133    MR2S OFFSET(8) NUMBITS(1) [],
134    /// Interrupt on MR3: an interrupt is generated when MR3 matches the value in the TC
135    MR3I OFFSET(9) NUMBITS(1) [],
136    /// Reset on MR3: the TC will be reset if MR3 matches it.
137    MR3R OFFSET(10) NUMBITS(1) [],
138    /// Stop on MR3: the TC and PC will be stopped and TCR[0] will be set to 0 if MR3 ma
139    MR3S OFFSET(11) NUMBITS(1) [],
140    /// Reload MR0 with the contents of the Match 0 Shadow Register when the TC is reset
141    MR0RL OFFSET(24) NUMBITS(1) [],
142    /// Reload MR1 with the contents of the Match 1 Shadow Register when the TC is reset
143    MR1RL OFFSET(25) NUMBITS(1) [],
144    /// Reload MR2 with the contents of the Match 2 Shadow Register when the TC is reset
145    MR2RL OFFSET(26) NUMBITS(1) [],
146    /// Reload MR3 with the contents of the Match 3 Shadow Register when the TC is reset
147    MR3RL OFFSET(27) NUMBITS(1) []
148],
149CCR [
150    /// Rising edge of capture channel 0: a sequence of 0 then 1 causes CR0 to be loaded
151    CAP0RE OFFSET(0) NUMBITS(1) [],
152    /// Falling edge of capture channel 0: a sequence of 1 then 0 causes CR0 to be loade
153    CAP0FE OFFSET(1) NUMBITS(1) [],
154    /// Generate interrupt on channel 0 capture event: a CR0 load generates an interrupt
155    CAP0I OFFSET(2) NUMBITS(1) [],
156    /// Rising edge of capture channel 1: a sequence of 0 then 1 causes CR1 to be loaded
157    CAP1RE OFFSET(3) NUMBITS(1) [],
158    /// Falling edge of capture channel 1: a sequence of 1 then 0 causes CR1 to be loade
159    CAP1FE OFFSET(4) NUMBITS(1) [],
160    /// Generate interrupt on channel 1 capture event: a CR1 load generates an interrupt
161    CAP1I OFFSET(5) NUMBITS(1) [],
162    /// Rising edge of capture channel 2: a sequence of 0 then 1 causes CR2 to be loaded
163    CAP2RE OFFSET(6) NUMBITS(1) [],
164    /// Falling edge of capture channel 2: a sequence of 1 then 0 causes CR2 to be loade
165    CAP2FE OFFSET(7) NUMBITS(1) [],
166    /// Generate interrupt on channel 2 capture event: a CR2 load generates an interrupt
167    CAP2I OFFSET(8) NUMBITS(1) [],
168    /// Rising edge of capture channel 3: a sequence of 0 then 1 causes CR3 to be loaded
169    CAP3RE OFFSET(9) NUMBITS(1) [],
170    /// Falling edge of capture channel 3: a sequence of 1 then 0 causes CR3 to be loade
171    CAP3FE OFFSET(10) NUMBITS(1) [],
172    /// Generate interrupt on channel 3 capture event: a CR3 load generates an interrupt
173    CAP3I OFFSET(11) NUMBITS(1) []
174],
175EMR [
176    /// External Match 0. This bit reflects the state of output MAT0, whether or not thi
177    EM0 OFFSET(0) NUMBITS(1) [],
178    /// External Match 1. This bit reflects the state of output MAT1, whether or not thi
179    EM1 OFFSET(1) NUMBITS(1) [],
180    /// External Match 2. This bit reflects the state of output MAT2, whether or not thi
181    EM2 OFFSET(2) NUMBITS(1) [],
182    /// External Match 3. This bit reflects the state of output MAT3, whether or not thi
183    EM3 OFFSET(3) NUMBITS(1) [],
184    /// External Match Control 0. Determines the functionality of External Match 0.
185    EMC0 OFFSET(4) NUMBITS(2) [
186        /// Do Nothing.
187        DoNothing = 0,
188        /// Clear. Clear the corresponding External Match bit/output to 0 (MAT0 pin is LOW i
189        ClearClearTheCorrespondingExternalMatchBitOutputTo0MAT0PinIsLOWIfPinnedOut = 1,
190        /// Set. Set the corresponding External Match bit/output to 1 (MAT0 pin is HIGH if p
191        SetSetTheCorrespondingExternalMatchBitOutputTo1MAT0PinIsHIGHIfPinnedOut = 2,
192        /// Toggle. Toggle the corresponding External Match bit/output.
193        ToggleToggleTheCorrespondingExternalMatchBitOutput = 3
194    ],
195    /// External Match Control 1. Determines the functionality of External Match 1.
196    EMC1 OFFSET(6) NUMBITS(2) [
197        /// Do Nothing.
198        DoNothing = 0,
199        /// Clear. Clear the corresponding External Match bit/output to 0 (MAT1 pin is LOW i
200        ClearClearTheCorrespondingExternalMatchBitOutputTo0MAT1PinIsLOWIfPinnedOut = 1,
201        /// Set. Set the corresponding External Match bit/output to 1 (MAT1 pin is HIGH if p
202        SetSetTheCorrespondingExternalMatchBitOutputTo1MAT1PinIsHIGHIfPinnedOut = 2,
203        /// Toggle. Toggle the corresponding External Match bit/output.
204        ToggleToggleTheCorrespondingExternalMatchBitOutput = 3
205    ],
206    /// External Match Control 2. Determines the functionality of External Match 2.
207    EMC2 OFFSET(8) NUMBITS(2) [
208        /// Do Nothing.
209        DoNothing = 0,
210        /// Clear. Clear the corresponding External Match bit/output to 0 (MAT2 pin is LOW i
211        ClearClearTheCorrespondingExternalMatchBitOutputTo0MAT2PinIsLOWIfPinnedOut = 1,
212        /// Set. Set the corresponding External Match bit/output to 1 (MAT2 pin is HIGH if p
213        SetSetTheCorrespondingExternalMatchBitOutputTo1MAT2PinIsHIGHIfPinnedOut = 2,
214        /// Toggle. Toggle the corresponding External Match bit/output.
215        ToggleToggleTheCorrespondingExternalMatchBitOutput = 3
216    ],
217    /// External Match Control 3. Determines the functionality of External Match 3.
218    EMC3 OFFSET(10) NUMBITS(2) [
219        /// Do Nothing.
220        DoNothing = 0,
221        /// Clear. Clear the corresponding External Match bit/output to 0 (MAT3 pin is LOW i
222        ClearClearTheCorrespondingExternalMatchBitOutputTo0MAT3PinIsLOWIfPinnedOut = 1,
223        /// Set. Set the corresponding External Match bit/output to 1 (MAT3 pin is HIGH if p
224        SetSetTheCorrespondingExternalMatchBitOutputTo1MAT3PinIsHIGHIfPinnedOut = 2,
225        /// Toggle. Toggle the corresponding External Match bit/output.
226        ToggleToggleTheCorrespondingExternalMatchBitOutput = 3
227    ]
228],
229CTCR [
230    /// Counter/Timer Mode This field selects which rising APB bus clock edges can incre
231    CTMODE OFFSET(0) NUMBITS(2) [
232        /// Timer Mode. Incremented every rising APB bus clock edge.
233        TimerModeIncrementedEveryRisingAPBBusClockEdge = 0,
234        /// Counter Mode rising edge. TC is incremented on rising edges on the CAP input sel
235        CounterModeRisingEdgeTCIsIncrementedOnRisingEdgesOnTheCAPInputSelectedByBits32 = 1,
236        /// Counter Mode falling edge. TC is incremented on falling edges on the CAP input s
237        COUNTER_FALLING_EDGE = 2,
238        /// Counter Mode dual edge. TC is incremented on both edges on the CAP input selecte
239        CounterModeDualEdgeTCIsIncrementedOnBothEdgesOnTheCAPInputSelectedByBits32 = 3
240    ],
241    /// Count Input Select When bits 1:0 in this register are not 00, these bits select
242    CINSEL OFFSET(2) NUMBITS(2) [
243        /// Channel 0. CAPn.0 for CTIMERn
244        Channel0CAPn0ForCTIMERn = 0,
245        /// Channel 1. CAPn.1 for CTIMERn
246        Channel1CAPn1ForCTIMERn = 1,
247        /// Channel 2. CAPn.2 for CTIMERn
248        Channel2CAPn2ForCTIMERn = 2,
249        /// Channel 3. CAPn.3 for CTIMERn
250        Channel3CAPn3ForCTIMERn = 3
251    ],
252    /// Setting this bit to 1 enables clearing of the timer and the prescaler when the c
253    ENCC OFFSET(4) NUMBITS(1) [],
254    /// Edge select. When bit 4 is 1, these bits select which capture input edge will ca
255    SELCC OFFSET(5) NUMBITS(3) [
256        /// Channel 0 Rising Edge. Rising edge of the signal on capture channel 0 clears the
257        CHANNEL_0_RISING = 0,
258        /// Channel 0 Falling Edge. Falling edge of the signal on capture channel 0 clears t
259        CHANNEL_0_FALLING = 1,
260        /// Channel 1 Rising Edge. Rising edge of the signal on capture channel 1 clears the
261        CHANNEL_1_RISING = 2,
262        /// Channel 1 Falling Edge. Falling edge of the signal on capture channel 1 clears t
263        CHANNEL_1_FALLING = 3,
264        /// Channel 2 Rising Edge. Rising edge of the signal on capture channel 2 clears the
265        CHANNEL_2_RISING = 4,
266        /// Channel 2 Falling Edge. Falling edge of the signal on capture channel 2 clears t
267        CHANNEL_2_FALLING = 5
268    ]
269],
270PWMC [
271    /// PWM mode enable for channel0.
272    PWMEN0 OFFSET(0) NUMBITS(1) [
273        /// Match. CTIMERn_MAT0 is controlled by EM0.
274        MatchCTIMERn_MAT0IsControlledByEM0 = 0,
275        /// PWM. PWM mode is enabled for CTIMERn_MAT0.
276        PWMPWMModeIsEnabledForCTIMERn_MAT0 = 1
277    ],
278    /// PWM mode enable for channel1.
279    PWMEN1 OFFSET(1) NUMBITS(1) [
280        /// Match. CTIMERn_MAT01 is controlled by EM1.
281        MatchCTIMERn_MAT01IsControlledByEM1 = 0,
282        /// PWM. PWM mode is enabled for CTIMERn_MAT1.
283        PWMPWMModeIsEnabledForCTIMERn_MAT1 = 1
284    ],
285    /// PWM mode enable for channel2.
286    PWMEN2 OFFSET(2) NUMBITS(1) [
287        /// Match. CTIMERn_MAT2 is controlled by EM2.
288        MatchCTIMERn_MAT2IsControlledByEM2 = 0,
289        /// PWM. PWM mode is enabled for CTIMERn_MAT2.
290        PWMPWMModeIsEnabledForCTIMERn_MAT2 = 1
291    ],
292    /// PWM mode enable for channel3. Note: It is recommended to use match channel 3 to
293    PWMEN3 OFFSET(3) NUMBITS(1) [
294        /// Match. CTIMERn_MAT3 is controlled by EM3.
295        MatchCTIMERn_MAT3IsControlledByEM3 = 0,
296        /// PWM. PWM mode is enabled for CT132Bn_MAT3.
297        PWMPWMModeIsEnabledForCT132Bn_MAT3 = 1
298    ]
299],
300/// Timer counter match value.
301MR0 [ MATCH OFFSET(0) NUMBITS(32) [] ],
302
303/// Timer counter match value.
304MR1 [ MATCH OFFSET(0) NUMBITS(32) [] ],
305
306/// Timer counter match value.
307MR2 [ MATCH OFFSET(0) NUMBITS(32) [] ],
308
309/// Timer counter match value.
310MR3 [ MATCH OFFSET(0) NUMBITS(32) [] ],
311
312/// Timer counter capture value.
313CR0 [ CAP OFFSET(0) NUMBITS(32) [] ],
314
315/// Timer counter capture value.
316CR1 [ CAP OFFSET(0) NUMBITS(32) [] ],
317
318/// Timer counter capture value.
319CR2 [ CAP OFFSET(0) NUMBITS(32) [] ],
320
321/// Timer counter capture value.
322CR3 [ CAP OFFSET(0) NUMBITS(32) [] ],
323
324/// Timer counter match shadow value.
325MSR0 [ SHADOW OFFSET(0) NUMBITS(32) [] ],
326
327/// Timer counter match shadow value.
328MSR1 [ SHADOW OFFSET(0) NUMBITS(32) [] ],
329
330/// Timer counter match shadow value.
331MSR2 [ SHADOW OFFSET(0) NUMBITS(32) [] ],
332
333/// Timer counter match shadow value.
334MSR3 [ SHADOW OFFSET(0) NUMBITS(32) [] ],
335];
336
337const CTIMER0_BASE: StaticRef<Ctimer0Registers> =
338    unsafe { StaticRef::new(0x50008000 as *const Ctimer0Registers) };
339
340pub struct LPCTimer<'a> {
341    registers: StaticRef<Ctimer0Registers>,
342    client: OptionalCell<&'a dyn hil::time::AlarmClient>,
343    armed: VolatileCell<bool>,
344}
345
346impl<'a> LPCTimer<'a> {
347    pub const fn new() -> LPCTimer<'a> {
348        LPCTimer {
349            registers: CTIMER0_BASE,
350            client: OptionalCell::empty(),
351            armed: VolatileCell::new(false),
352        }
353    }
354
355    pub fn init(&self, pclk_hz: u32) {
356        let pr = pclk_hz / 1_000_000 - 1;
357
358        self.registers.tcr.modify(TCR::CRST::SET);
359        self.registers.tcr.modify(TCR::CRST::CLEAR);
360
361        self.registers.tcr.set(0x1);
362
363        self.registers.ctcr.set(0);
364
365        self.registers.pr.set(pr);
366
367        self.registers.pc.set(0);
368
369        self.registers.tcr.modify(TCR::CEN::SET);
370
371        self.registers.mr_0.set(250_000);
372
373        self.registers
374            .mcr
375            .modify(MCR::MR0R::CLEAR + MCR::MR0S::CLEAR + MCR::MR0I::CLEAR);
376
377        self.registers.ir.modify(IR::MR0INT::SET);
378    }
379
380    fn enable_interrupt(&self) {
381        self.registers.mcr.modify(MCR::MR0I::SET);
382    }
383
384    fn disable_interrupt(&self) {
385        self.registers.mcr.modify(MCR::MR0I::CLEAR);
386    }
387
388    fn enable_timer_interrupt(&self) {
389        unsafe {
390            with_interrupts_disabled(|| {
391                let n = cortexm33::nvic::Nvic::new(CTIMER0);
392                n.enable();
393            })
394        }
395    }
396
397    #[allow(dead_code)]
398    fn disable_timer_interrupt(&self) {
399        unsafe {
400            cortexm33::nvic::Nvic::new(CTIMER0).disable();
401        }
402    }
403
404    pub fn handle_interrupt(&self) {
405        self.registers.ir.modify(IR::MR0INT::SET);
406
407        self.armed.set(false);
408
409        self.client.map(|client| client.alarm());
410    }
411
412    pub fn get_pr(&self) -> u32 {
413        self.registers.pr.get()
414    }
415}
416
417impl Time for LPCTimer<'_> {
418    type Frequency = hil::time::Freq1MHz;
419    type Ticks = Ticks32;
420
421    fn now(&self) -> Self::Ticks {
422        self.registers.tcr.set(0x1);
423
424        Self::Ticks::from(self.registers.tc.get())
425    }
426}
427
428impl<'a> Alarm<'a> for LPCTimer<'a> {
429    fn set_alarm_client(&self, client: &'a dyn hil::time::AlarmClient) {
430        self.client.set(client)
431    }
432
433    fn set_alarm(&self, reference: Self::Ticks, dt: Self::Ticks) {
434        let mut expire = reference.wrapping_add(dt);
435        let now = self.now();
436
437        if !now.within_range(reference, expire) {
438            expire = now;
439        }
440
441        let min = self.minimum_dt();
442        if expire.wrapping_sub(now) < min {
443            expire = now.wrapping_add(min);
444        }
445
446        self.registers.mr_0.set(expire.into_u32());
447
448        self.registers.ir.modify(IR::MR0INT::SET);
449
450        self.enable_interrupt();
451        self.enable_timer_interrupt();
452
453        self.armed.set(true);
454    }
455
456    fn get_alarm(&self) -> Self::Ticks {
457        Self::Ticks::from(self.registers.mr_0.get())
458    }
459
460    fn disarm(&self) -> Result<(), kernel::ErrorCode> {
461        self.disable_interrupt();
462        self.armed.set(false);
463
464        unsafe {
465            with_interrupts_disabled(|| {
466                cortexm33::nvic::Nvic::new(CTIMER0).clear_pending();
467            });
468        }
469
470        Ok(())
471    }
472
473    fn is_armed(&self) -> bool {
474        self.armed.get()
475    }
476
477    fn minimum_dt(&self) -> Self::Ticks {
478        Self::Ticks::from(50)
479    }
480}