apollo3/
stimer.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 2022.
4
5//! STimer driver for the Apollo3
6
7use kernel::hil::time::{
8    Alarm, AlarmClient, Counter, Freq16KHz, OverflowClient, Ticks, Ticks32, Time,
9};
10use kernel::utilities::cells::OptionalCell;
11use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
12use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite};
13use kernel::utilities::StaticRef;
14use kernel::ErrorCode;
15
16const STIMER_BASE: StaticRef<STimerRegisters> =
17    unsafe { StaticRef::new(0x4000_8000 as *const STimerRegisters) };
18
19register_structs! {
20    pub STimerRegisters {
21        (0x000 => _reserved0),
22        (0x140 => stcfg: ReadWrite<u32, STCFG::Register>),
23        (0x144 => sttmr: ReadWrite<u32, STTMR::Register>),
24        (0x148 => capturecontrol: ReadWrite<u32, CAPTURECONTROL::Register>),
25        (0x14C => _reserved1),
26        (0x150 => scmpr: [ReadWrite<u32, SCMPR::Register>; 8]),
27        (0x170 => _reserved2),
28        (0x1E0 => scapt: [ReadWrite<u32, SCAPT::Register>; 4]),
29        (0x1F0 => snvr: [ReadWrite<u32, SNVR::Register>; 4]),
30        (0x200 => _reserved3),
31        (0x300 => stminten: ReadWrite<u32, STMINT::Register>),
32        (0x304 => stmintstat: ReadWrite<u32, STMINT::Register>),
33        (0x308 => stmintclr: ReadWrite<u32, STMINT::Register>),
34        (0x30C => stmintset: ReadWrite<u32, STMINT::Register>),
35        (0x310 => @END),
36    }
37}
38
39register_bitfields![u32,
40    STCFG [
41        CLKSEL OFFSET(0) NUMBITS(4) [
42            NOCLK = 0x0,
43            HRFC_DIV16 = 0x1,
44            HRFC_DIV256 = 0x2,
45            XTAL_DIV1 = 0x3,
46            XTAL_DIV2 = 0x4,
47            XTAL_DIV32 = 0x5,
48            LFRC_DIV1 = 0x6,
49            CTIMER0A = 0x7,
50            CTIMER0B = 0x8
51        ],
52        COMPARE_A_EN OFFSET(8) NUMBITS(1) [],
53        COMPARE_B_EN OFFSET(9) NUMBITS(1) [],
54        COMPARE_C_EN OFFSET(10) NUMBITS(1) [],
55        COMPARE_D_EN OFFSET(11) NUMBITS(1) [],
56        COMPARE_E_EN OFFSET(12) NUMBITS(1) [],
57        COMPARE_F_EN OFFSET(13) NUMBITS(1) [],
58        COMPARE_G_EN OFFSET(14) NUMBITS(1) [],
59        COMPARE_H_EN OFFSET(15) NUMBITS(1) [],
60        CLEAR OFFSET(30) NUMBITS(1) [],
61        FREEZE OFFSET(31) NUMBITS(1) []
62    ],
63    STTMR [
64        STTMR OFFSET(0) NUMBITS(31) []
65    ],
66    CAPTURECONTROL [
67        CAPTURE0 OFFSET(0) NUMBITS(1) [],
68        CAPTURE1 OFFSET(1) NUMBITS(1) [],
69        CAPTURE2 OFFSET(2) NUMBITS(1) [],
70        CAPTURE3 OFFSET(3) NUMBITS(1) []
71    ],
72    SCMPR [
73        SCMPR OFFSET(0) NUMBITS(31) []
74    ],
75    SCAPT [
76        SCATP OFFSET(0) NUMBITS(31) []
77    ],
78    SNVR [
79        SNVR OFFSET(0) NUMBITS(31) []
80    ],
81    STMINT [
82        COMPAREA OFFSET(0) NUMBITS(1) [],
83        COMPAREB OFFSET(1) NUMBITS(1) [],
84        COMPAREC OFFSET(2) NUMBITS(1) [],
85        COMPARED OFFSET(3) NUMBITS(1) [],
86        COMPAREE OFFSET(4) NUMBITS(1) [],
87        COMPAREF OFFSET(5) NUMBITS(1) [],
88        COMPAREG OFFSET(6) NUMBITS(1) [],
89        COMPAREH OFFSET(7) NUMBITS(1) [],
90        OVERFLOW OFFSET(8) NUMBITS(1) [],
91        CAPTUREA OFFSET(9) NUMBITS(1) [],
92        CAPTUREB OFFSET(10) NUMBITS(1) [],
93        CAPTUREC OFFSET(11) NUMBITS(1) [],
94        CAPTURED OFFSET(12) NUMBITS(1) []
95    ]
96];
97
98pub struct STimer<'a> {
99    registers: StaticRef<STimerRegisters>,
100    client: OptionalCell<&'a dyn AlarmClient>,
101}
102
103impl<'a> STimer<'a> {
104    // Unsafe bc of use of STIMER_BASE internally
105    pub fn new() -> STimer<'a> {
106        let timer = STimer {
107            registers: STIMER_BASE,
108            client: OptionalCell::empty(),
109        };
110
111        // Reset so that time starts at 0
112        let _ = timer.reset();
113
114        timer
115    }
116
117    pub fn handle_interrupt(&self) {
118        let regs = self.registers;
119
120        // Disable timer
121        regs.stcfg
122            .modify(STCFG::COMPARE_A_EN::CLEAR + STCFG::COMPARE_B_EN::CLEAR);
123
124        // Disable interrupt
125        regs.stminten
126            .modify(STMINT::COMPAREA::CLEAR + STMINT::COMPAREB::CLEAR);
127
128        // Clear interrupt
129        regs.stmintclr
130            .modify(STMINT::COMPAREA::SET + STMINT::COMPAREB::SET);
131
132        self.client.map(|client| client.alarm());
133    }
134}
135
136impl Time for STimer<'_> {
137    type Frequency = Freq16KHz;
138    type Ticks = Ticks32;
139
140    fn now(&self) -> Ticks32 {
141        Ticks32::from(self.registers.sttmr.get())
142    }
143}
144
145impl<'a> Counter<'a> for STimer<'a> {
146    fn set_overflow_client(&self, _client: &'a dyn OverflowClient) {
147        //self.overflow_client.set(client);
148    }
149
150    fn start(&self) -> Result<(), ErrorCode> {
151        // Set the clock source
152        self.registers.stcfg.write(STCFG::CLKSEL::XTAL_DIV2);
153        Ok(())
154    }
155
156    fn stop(&self) -> Result<(), ErrorCode> {
157        Err(ErrorCode::BUSY)
158    }
159
160    fn reset(&self) -> Result<(), ErrorCode> {
161        self.registers.stcfg.write(STCFG::CLEAR::SET);
162        Ok(())
163    }
164
165    fn is_running(&self) -> bool {
166        let regs = self.registers;
167        regs.stcfg.matches_any(&[STCFG::CLKSEL::XTAL_DIV2])
168    }
169}
170
171impl<'a> Alarm<'a> for STimer<'a> {
172    fn set_alarm_client(&self, client: &'a dyn AlarmClient) {
173        self.client.set(client);
174    }
175
176    fn set_alarm(&self, reference: Self::Ticks, dt: Self::Ticks) {
177        let regs = self.registers;
178        let now = self.now();
179        // Errata 4.22: Sometimes the clock can increment twice
180        // This means the timer occurs earlier then actually requested
181        // From testing this scaling results in the correct time, so we
182        // scale the requested ticks to give us an accurate alarm.
183        let scaled_time = Self::Ticks::from(((dt.into_u32() as u64 * 1000) / (1000 - 32)) as u32);
184        let expire = reference.wrapping_add(scaled_time);
185
186        // Disable the compare
187        regs.stcfg
188            .modify(STCFG::COMPARE_A_EN::CLEAR + STCFG::COMPARE_B_EN::CLEAR);
189
190        // Enable interrupts
191        regs.stminten
192            .modify(STMINT::COMPAREA::SET + STMINT::COMPAREB::SET);
193
194        // Check if the alarm has already expired or if it will expire before we set
195        // the compare.
196        if !now.within_range(reference, expire) || expire.wrapping_sub(now) < self.minimum_dt() {
197            // The alarm has already expired!
198            // Let's set the interrupt manually
199            regs.stcfg.modify(STCFG::COMPARE_A_EN::SET);
200            regs.stmintset.modify(STMINT::COMPAREA::SET);
201            return;
202        }
203
204        // Set the delta, this can take a few goes
205        // See Errata 4.14 at at https://ambiq.com/wp-content/uploads/2022/01/Apollo3-Blue-Errata-List.pdf
206        let mut timer_delta = expire.wrapping_sub(now);
207        let mut tries = 0;
208
209        // Apollo3 Blue Datasheet 14.1: 'Only offsets from "NOW" are written to
210        // comparator registers.'
211        while Self::Ticks::from(regs.scmpr[0].get()) != expire && tries < 5 {
212            regs.scmpr[0].set(timer_delta.into_u32());
213            tries += 1;
214        }
215
216        // Timers can be missed, so set a second one a little larger
217        // See Errata 4.22 at at https://ambiq.com/wp-content/uploads/2022/01/Apollo3-Blue-Errata-List.pdf
218        timer_delta = timer_delta.wrapping_add(1.into());
219        tries = 0;
220
221        while Self::Ticks::from(regs.scmpr[1].get()) != expire && tries < 5 {
222            regs.scmpr[1].set(timer_delta.into_u32());
223            tries += 1;
224        }
225
226        // Enable the compare
227        regs.stcfg
228            .modify(STCFG::COMPARE_A_EN::SET + STCFG::COMPARE_B_EN::SET);
229    }
230
231    fn get_alarm(&self) -> Self::Ticks {
232        let regs = self.registers;
233        Self::Ticks::from(regs.scmpr[0].get())
234    }
235
236    fn disarm(&self) -> Result<(), ErrorCode> {
237        let regs = self.registers;
238
239        regs.stcfg.modify(
240            STCFG::COMPARE_A_EN::CLEAR
241                + STCFG::COMPARE_B_EN::CLEAR
242                + STCFG::COMPARE_C_EN::CLEAR
243                + STCFG::COMPARE_D_EN::CLEAR
244                + STCFG::COMPARE_E_EN::CLEAR
245                + STCFG::COMPARE_F_EN::CLEAR
246                + STCFG::COMPARE_G_EN::CLEAR
247                + STCFG::COMPARE_H_EN::CLEAR,
248        );
249        Ok(())
250    }
251
252    fn is_armed(&self) -> bool {
253        let regs = self.registers;
254
255        regs.stcfg.read(STCFG::COMPARE_A_EN) != 0
256    }
257
258    fn minimum_dt(&self) -> Self::Ticks {
259        Self::Ticks::from(5)
260    }
261}