Skip to main content

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