1#![no_std]
5#![doc = include_str!("../README.md")]
6#![deny(clippy::undocumented_unsafe_blocks)]
7#![deny(unsafe_op_in_unsafe_fn)]
8
9use core::time::Duration;
10
11use bitflags::bitflags;
12use safe_mmio::{
13 field, field_shared,
14 fields::{ReadPure, ReadPureWrite},
15 UniqueMmioPointer,
16};
17use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
18
19#[repr(transparent)]
21#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
22pub struct CntCr(u32);
23
24#[repr(transparent)]
26#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
27pub struct CntSr(u32);
28
29#[repr(transparent)]
31#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
32pub struct CntId(u32);
33
34#[repr(transparent)]
36#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
37pub struct CntAcr(u32);
38
39#[repr(transparent)]
41#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
42pub struct Features(u8);
43
44#[repr(transparent)]
46#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
47pub struct CntEl0Acr(u32);
48
49#[repr(transparent)]
52#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
53pub struct TimerControl(u32);
54
55bitflags! {
56 impl CntCr: u32 {
57 const SCEN = 1 << 2;
59 const HDBG = 1 << 1;
61 const EN = 1 << 0;
63 }
64
65 impl CntSr: u32 {
66 const HDBG = 1 << 1;
68 }
69
70 impl CntAcr: u32 {
71 const RWPT = 1 << 5;
73 const RWVT = 1 << 4;
75 const RVOFF = 1 << 3;
77 const RFRQ = 1 << 2;
79 const RVCT = 1 << 1;
81 const RPCT = 1 << 0;
83 }
84
85 impl Features: u8 {
86 const CNTEL0BASE = 1 << 2;
88 const VIRTUAL = 1 << 1;
90 const IMPLEMENTED = 1 << 0;
92 }
93
94 impl CntEl0Acr: u32 {
95 const EL0PTEN = 1 << 9;
97 const EL0VTEN = 1 << 8;
99 const EL0VCTEN = 1 << 1;
101 const EL0PCTEN = 1 << 0;
103 }
104
105 impl TimerControl: u32 {
106 const ISTATUS = 1 << 2;
108 const IMASK = 1 << 1;
110 const ENABLE = 1 << 0;
112 }
113}
114
115impl CntCr {
116 const FCREQ_MASK: u32 = 0x0000_03ff;
117 const FCREQ_SHIFT: u32 = 8;
118
119 pub fn set_fcreq(&mut self, index: usize) {
121 let mut value = self.0 & !(Self::FCREQ_MASK << Self::FCREQ_SHIFT);
122 value |= ((index as u32) & Self::FCREQ_MASK) << Self::FCREQ_SHIFT;
123 self.0 = value;
124 }
125}
126
127impl CntSr {
128 const FCACK_MASK: u32 = 0x0000_03ff;
129 const FCACK_SHIFT: u32 = 8;
130
131 pub fn fcack(&self) -> usize {
133 ((self.0 >> Self::FCACK_SHIFT) & Self::FCACK_MASK) as usize
134 }
135}
136
137impl CntId {
138 const CNTSC_MASK: u32 = 0b1111;
139 const CNTSC_IMPLEMENTED: u32 = 0b0001;
140
141 pub fn scaling_implemented(&self) -> bool {
143 self.0 & Self::CNTSC_MASK == Self::CNTSC_IMPLEMENTED
144 }
145}
146
147#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
149#[repr(C, align(4))]
150pub struct CntControlBase {
151 cntcr: ReadPureWrite<CntCr>,
153 cntsr: ReadPure<CntSr>,
155 cntcv: ReadPureWrite<u64>,
157 cntscr: ReadPureWrite<u32>,
159 reserved_14: [u32; 2],
160 cntid: ReadPure<CntId>,
162 cntfid: [ReadPureWrite<u32>; 40],
164 impdef_0c0: [u32; 16],
166 reserved_100: [u32; 948],
167 counter_id: [ReadPure<u32>; 12],
169}
170
171#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
173#[repr(C, align(4))]
174pub struct CntReadBase {
175 cntcv: ReadPure<u64>,
177 reserved_8: [u32; 1010],
178 counter_id: [ReadPure<u32>; 12],
180}
181
182#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
184#[repr(C, align(4))]
185pub struct CntCtlBase {
186 cntfrq: ReadPureWrite<u32>,
188 cntnsar: ReadPureWrite<u32>,
190 cnttidr: ReadPure<u32>,
192 reserved_00c: [u32; 13],
193 cntacr: [ReadPureWrite<CntAcr>; 8],
195 reserved_060: [u32; 8],
196 cntvoff: [ReadPureWrite<u64>; 8],
198 reserved_0c0: [u32; 16],
199 impdef_100: [u32; 448],
201 reserved_800: [u32; 496],
202 impdef_fc0: [u32; 4],
203 counter_id: [ReadPure<u32>; 12],
205}
206
207#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
210#[repr(C, align(4))]
211pub struct TimerRegs {
212 cval: ReadPureWrite<u64>,
214 tval: ReadPureWrite<u32>,
216 ctl: ReadPureWrite<TimerControl>,
218}
219
220#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
222#[repr(C, align(4))]
223pub struct CntBase {
224 cntpct: ReadPure<u64>,
226 cntvct: ReadPure<u64>,
228 cntfrq: ReadPure<u32>,
230 cntel0acr: ReadPureWrite<CntEl0Acr>,
232 cntvoff: ReadPure<u64>,
234 cntp: TimerRegs,
236 cntv: TimerRegs,
238 reserved: [u32; 996],
239 counter_id: [ReadPure<u32>; 12],
241}
242
243#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
247#[repr(C, align(4))]
248pub struct CntEl0Base {
249 cntpct: ReadPure<u64>,
251 cntvct: ReadPure<u64>,
253 cntfrq: ReadPure<u32>,
255 reserved_014: [u32; 3],
256 cntp: TimerRegs,
258 cntv: TimerRegs,
260 reserved: [u32; 996],
261 counter_id: [ReadPure<u32>; 12],
263}
264
265pub struct GenericTimerControl<'a> {
267 regs: UniqueMmioPointer<'a, CntControlBase>,
268}
269
270impl<'a> GenericTimerControl<'a> {
271 pub fn new(regs: UniqueMmioPointer<'a, CntControlBase>) -> Self {
273 Self { regs }
274 }
275
276 pub fn set_enable(&mut self, enable: bool) {
278 let mut cntcr = field!(self.regs, cntcr).read();
279 cntcr.set(CntCr::EN, enable);
280 field!(self.regs, cntcr).write(cntcr);
281 }
282
283 pub fn request_frequency(&mut self, index: usize) {
285 let mut cntcr = field!(self.regs, cntcr).read();
286 cntcr.set_fcreq(index);
287 field!(self.regs, cntcr).write(cntcr);
288 }
289
290 pub fn frequency_index(&self) -> usize {
292 field_shared!(self.regs, cntsr).read().fcack()
293 }
294
295 pub fn count(&self) -> u64 {
297 field_shared!(self.regs, cntcv).read()
298 }
299
300 pub fn set_count(&mut self, count: u64) {
302 field!(self.regs, cntcv).write(count);
303 }
304
305 pub fn scaling_implemented(&self) -> bool {
307 field_shared!(self.regs, cntid).read().scaling_implemented()
308 }
309
310 pub fn scale(&self) -> u32 {
312 field_shared!(self.regs, cntscr).read()
313 }
314
315 pub fn enable_scaling(&mut self, scale: u32) {
317 field!(self.regs, cntscr).write(scale);
318 let cntcr = field!(self.regs, cntcr).read();
319 field!(self.regs, cntcr).write(cntcr | CntCr::SCEN);
320 }
321
322 pub fn disable_scaling(&mut self) {
324 let cntcr = field!(self.regs, cntcr).read();
325 field!(self.regs, cntcr).write(cntcr - CntCr::SCEN);
326 field!(self.regs, cntscr).write(0);
327 }
328
329 pub fn base_frequency(&self) -> u32 {
331 field_shared!(self.regs, cntfid).get(0).unwrap().read()
332 }
333
334 pub fn frequency_mode(&self, index: usize) -> Option<u32> {
337 let frequency = field_shared!(self.regs, cntfid).get(index).unwrap().read();
338
339 if frequency != 0 {
340 Some(frequency)
341 } else {
342 None
343 }
344 }
345
346 pub fn set_frequency_mode(&mut self, index: usize, frequency: u32) {
349 field!(self.regs, cntfid)
350 .get(index)
351 .unwrap()
352 .write(frequency)
353 }
354}
355
356pub struct GenericTimerCtl<'a> {
358 regs: UniqueMmioPointer<'a, CntCtlBase>,
359}
360
361impl<'a> GenericTimerCtl<'a> {
362 pub fn new(regs: UniqueMmioPointer<'a, CntCtlBase>) -> Self {
364 Self { regs }
365 }
366
367 pub fn frequency(&self) -> u32 {
369 field_shared!(self.regs, cntfrq).read()
370 }
371
372 pub fn set_frequency(&mut self, frequency: u32) {
374 field!(self.regs, cntfrq).write(frequency);
375 }
376
377 pub fn non_secure_access(&self, index: usize) -> bool {
379 assert!(index < 8);
380
381 let cntnsar = field_shared!(self.regs, cntnsar).read();
382 cntnsar & (1 << index) != 0
383 }
384
385 pub fn set_non_secure_access(&mut self, index: usize, enable: bool) {
388 assert!(index < 8);
389
390 let mut cntnsar = field_shared!(self.regs, cntnsar).read();
391 if enable {
392 cntnsar |= 1 << index;
393 } else {
394 cntnsar &= !(1 << index);
395 }
396 field!(self.regs, cntnsar).write(cntnsar);
397 }
398
399 pub fn features(&self, index: usize) -> Features {
401 assert!(index < 8);
402
403 let cnttidr = field_shared!(self.regs, cnttidr).read();
404 Features::from_bits_truncate((cnttidr >> (index * 8)) as u8)
405 }
406
407 pub fn access_control(&self, index: usize) -> CntAcr {
409 field_shared!(self.regs, cntacr).get(index).unwrap().read()
410 }
411
412 pub fn set_access_control(&mut self, index: usize, cntacr: CntAcr) {
414 field!(self.regs, cntacr).get(index).unwrap().write(cntacr);
415 }
416
417 pub fn virtual_offset(&self, index: usize) -> u64 {
419 field_shared!(self.regs, cntvoff).get(index).unwrap().read()
420 }
421
422 pub fn set_virtual_offset(&mut self, index: usize, offset: u64) {
425 field!(self.regs, cntvoff).get(index).unwrap().write(offset);
426 }
427}
428
429pub struct Timer<'a> {
431 regs: UniqueMmioPointer<'a, TimerRegs>,
432 frequency: u32,
433}
434
435impl<'a> Timer<'a> {
436 pub fn new(regs: UniqueMmioPointer<'a, TimerRegs>, frequency: u32) -> Self {
438 Self { regs, frequency }
439 }
440
441 pub unsafe fn generate_interrupt_after(&mut self, duration: Duration) {
448 self.set_deadline(duration);
449 self.set_control(TimerControl::ENABLE);
450 }
451
452 pub fn cancel_interrupt(&mut self) {
454 self.set_control(TimerControl::IMASK);
455 }
456
457 pub fn wait(&mut self, duration: Duration) {
459 self.set_deadline(duration);
460 self.set_control(TimerControl::ENABLE | TimerControl::IMASK);
461
462 while !self.control().contains(TimerControl::ISTATUS) {
463 core::hint::spin_loop();
464 }
465 }
466
467 fn set_deadline(&mut self, duration: Duration) {
469 let increment = self.frequency as u64 * duration.as_micros() as u64 / 1_000_000;
470
471 let value = field!(self.regs, cval).read();
472 field!(self.regs, cval).write(value + increment);
473 }
474
475 fn control(&self) -> TimerControl {
477 field_shared!(self.regs, ctl).read()
478 }
479
480 fn set_control(&mut self, control: TimerControl) {
482 field!(self.regs, ctl).write(control)
483 }
484}
485
486pub struct GenericTimerCnt<'a> {
488 regs: UniqueMmioPointer<'a, CntBase>,
489}
490
491impl<'a> GenericTimerCnt<'a> {
492 pub fn new(regs: UniqueMmioPointer<'a, CntBase>) -> Self {
494 Self { regs }
495 }
496
497 pub fn physical_count(&self) -> u64 {
499 field_shared!(self.regs, cntpct).read()
500 }
501
502 pub fn virtual_count(&self) -> u64 {
504 field_shared!(self.regs, cntvct).read()
505 }
506
507 pub fn frequency(&self) -> u32 {
509 field_shared!(self.regs, cntfrq).read()
510 }
511
512 pub fn el0_access(&self) -> CntEl0Acr {
514 field_shared!(self.regs, cntel0acr).read()
515 }
516
517 pub fn set_el0_access(&mut self, value: CntEl0Acr) {
519 field!(self.regs, cntel0acr).write(value)
520 }
521
522 pub fn virtual_offset(&self) -> u64 {
524 field_shared!(self.regs, cntvoff).read()
525 }
526
527 pub fn physical_timer(&mut self) -> Timer<'_> {
529 let frequency = self.frequency();
530 Timer::new(field!(self.regs, cntp), frequency)
531 }
532
533 pub fn virtual_timer(&mut self) -> Timer<'_> {
535 let frequency = self.frequency();
536 Timer::new(field!(self.regs, cntv), frequency)
537 }
538}
539
540pub struct GenericTimerCntEl0<'a> {
542 regs: UniqueMmioPointer<'a, CntEl0Base>,
543}
544
545impl<'a> GenericTimerCntEl0<'a> {
546 pub fn new(regs: UniqueMmioPointer<'a, CntEl0Base>) -> Self {
548 Self { regs }
549 }
550
551 pub fn physical_count(&self) -> u64 {
553 field_shared!(self.regs, cntpct).read()
554 }
555
556 pub fn virtual_count(&self) -> u64 {
558 field_shared!(self.regs, cntvct).read()
559 }
560
561 pub fn frequency(&self) -> u32 {
563 field_shared!(self.regs, cntfrq).read()
564 }
565
566 pub fn physical_timer(&mut self) -> Timer<'_> {
568 let frequency = self.frequency();
569 Timer::new(field!(self.regs, cntp), frequency)
570 }
571
572 pub fn virtual_timer(&mut self) -> Timer<'_> {
574 let frequency = self.frequency();
575 Timer::new(field!(self.regs, cntv), frequency)
576 }
577}
578
579#[cfg(test)]
580mod tests {
581 use super::*;
582
583 #[test]
584 fn sizes() {
585 assert_eq!(0x1000, core::mem::size_of::<CntControlBase>());
586 assert_eq!(0x1000, core::mem::size_of::<CntReadBase>());
587 assert_eq!(0x1000, core::mem::size_of::<CntCtlBase>());
588 assert_eq!(0x1000, core::mem::size_of::<CntBase>());
589 assert_eq!(0x1000, core::mem::size_of::<CntEl0Base>());
590 }
591}