1use zerocopy::{FromBytes, FromZeros, Immutable, IntoBytes};
6
7pub trait RegisterValue: Sized + FromBytes + IntoBytes + FromZeros + Immutable {}
13
14pub trait RegisterBlock {
16 const ADDRESS_UNIT_BYTES: u16 = 1;
19}
20
21#[doc(hidden)]
23#[inline(always)]
24pub const fn block_address_unit_bytes<B: RegisterBlock>(_: &B) -> u16 {
25 B::ADDRESS_UNIT_BYTES
26}
27
28pub trait SpmiRegisterDef {
30 type Value: RegisterValue;
32
33 type Mode;
35
36 const ADDRESS: u16;
38}
39
40pub trait SpmiRegister<D>: SpmiRegisterDef {
44 type Accessor<'a>
46 where
47 Self: 'a,
48 D: 'a;
49
50 fn get_accessor<'a>(spmi: &'a D) -> Self::Accessor<'a>;
52}
53
54#[doc(hidden)]
56#[inline(always)]
57pub const fn assert_contiguous<B, R1, R2>(_: &B)
58where
59 B: RegisterBlock,
60 R1: SpmiRegisterDef,
61 R2: SpmiRegisterDef,
62{
63 struct ContiguityCheck<B, R1, R2>(std::marker::PhantomData<(B, R1, R2)>);
64
65 impl<B, R1, R2> ContiguityCheck<B, R1, R2>
66 where
67 B: RegisterBlock,
68 R1: SpmiRegisterDef,
69 R2: SpmiRegisterDef,
70 {
71 const ASSERT: () = {
72 let unit = B::ADDRESS_UNIT_BYTES as u32;
73 let val_size = std::mem::size_of::<R1::Value>() as u32;
74 let r1_addr = R1::ADDRESS as u32;
75 let r2_addr = R2::ADDRESS as u32;
76 let step = (val_size + unit - 1) / unit;
77 assert!(r2_addr == r1_addr + step, "Registers are not contiguous");
78 };
79 }
80
81 let _ = ContiguityCheck::<B, R1, R2>::ASSERT;
82}
83
84pub struct ReadOnly;
91
92pub struct WriteOnly;
94
95pub struct ReadWrite;
97
98pub trait Readable {}
103impl Readable for ReadOnly {}
104impl Readable for ReadWrite {}
105
106pub trait Writable {}
111impl Writable for WriteOnly {}
112impl Writable for ReadWrite {}
113
114pub struct _Register<'a, T, M, D, const ADDR: u16> {
123 pub spmi: &'a D,
125 _marker: std::marker::PhantomData<(T, M)>,
126}
127
128impl<'a, T, M, D, const ADDR: u16> _Register<'a, T, M, D, ADDR> {
129 pub fn new(spmi: &'a D) -> Self {
131 Self { spmi, _marker: std::marker::PhantomData }
132 }
133}
134
135impl<'a, T, M, D, const ADDR: u16> _Register<'a, T, M, D, ADDR>
137where
138 M: Readable,
139 T: RegisterValue,
140 D: SpmiDevice,
141{
142 pub async fn read(&self) -> Result<T, crate::Error> {
147 let size = std::mem::size_of::<T>();
148 let bytes = self.spmi.read_reg(ADDR, size as u32).await?;
149 T::read_from_bytes(&bytes).map_err(|_| crate::Error::SizeMismatch)
150 }
151}
152
153impl<'a, T, M, D, const ADDR: u16> _Register<'a, T, M, D, ADDR>
155where
156 M: Writable,
157 T: RegisterValue,
158 D: SpmiDevice,
159{
160 pub async fn write(&self, val: T) -> Result<(), crate::Error> {
165 let bytes = val.as_bytes();
166 self.spmi.write_reg(ADDR, bytes).await?;
167 Ok(())
168 }
169}
170
171#[macro_export]
272macro_rules! spmi_register {
273 (@mode_type RO) => { $crate::ReadOnly };
275 (@mode_type WO) => { $crate::WriteOnly };
276 (@mode_type RW) => { $crate::ReadWrite };
277
278 (@byteorder_type u8, $endianness:ident) => { u8 };
280 (@byteorder_type u16, LE) => { $crate::U16<$crate::LittleEndian> };
281 (@byteorder_type u16, BE) => { $crate::U16<$crate::BigEndian> };
282 (@byteorder_type u32, LE) => { $crate::U32<$crate::LittleEndian> };
283 (@byteorder_type u32, BE) => { $crate::U32<$crate::BigEndian> };
284
285 (@new u8, $endianness:ident, $val:expr) => { Self($val) };
286 (@new u16, $endianness:ident, $val:expr) => {
287 Self($crate::U16::new($val))
288 };
289 (@new u32, $endianness:ident, $val:expr) => {
290 Self($crate::U32::new($val))
291 };
292
293 (@get u8, $endianness:ident, $val:expr) => { $val };
294 (@get u16, $endianness:ident, $val:expr) => { $val.get() };
295 (@get u32, $endianness:ident, $val:expr) => { $val.get() };
296
297 (
299 $name:ident,
300 $value_type:ident,
301 $addr:expr,
302 $mode:ident,
303 $endianness:ident,
304 {
305 $($tail:tt)*
306 }
307 ) => {
308 #[allow(unused_imports)]
309 #[allow(dead_code)]
310 pub mod $name {
311 use super::*;
312 use $crate::zerocopy_reexport as zerocopy;
313
314 pub const ADDRESS: u16 = $addr;
316
317 $crate::spmi_register_extract_enums!($value_type, $($tail)*);
318
319 #[derive(
321 Copy,
322 Clone,
323 Debug,
324 PartialEq,
325 Eq,
326 zerocopy::FromBytes,
327 zerocopy::IntoBytes,
328 zerocopy::Immutable,
329 )]
330 #[repr(transparent)]
331 pub struct Value(
332 pub spmi_register!(@byteorder_type $value_type, $endianness),
333 );
334
335 impl Value {
336 pub const fn new(val: $value_type) -> Self {
337 spmi_register!(@new $value_type, $endianness, val)
338 }
339 pub const fn reg_value(&self) -> $value_type {
340 spmi_register!(@get $value_type, $endianness, self.0)
341 }
342
343 pub fn from_bytes(bytes: &[u8]) -> Result<Self, $crate::Error> {
345 zerocopy::FromBytes::read_from_bytes(bytes)
346 .map_err(|_| $crate::Error::SizeMismatch)
347 }
348
349 pub fn to_bytes(
351 &self,
352 ) -> [u8; std::mem::size_of::<$value_type>()] {
353 let mut arr = [0u8; std::mem::size_of::<$value_type>()];
354 arr.copy_from_slice($crate::IntoBytes::as_bytes(self));
355 arr
356 }
357
358 $crate::spmi_register_fields!($value_type, $($tail)*);
359 }
360
361 impl $crate::RegisterValue for Value {}
362
363 impl Default for Value {
364 fn default() -> Self {
365 Self::new(0 as $value_type)
366 }
367 }
368
369 impl $crate::SpmiRegisterDef for Value {
370 type Value = Self;
371 type Mode = spmi_register!(@mode_type $mode);
372 const ADDRESS: u16 = $addr;
373 }
374
375 impl<D> $crate::SpmiRegister<D> for Value {
376 type Accessor<'a> =
377 $crate::_Register<'a, Self, spmi_register!(@mode_type $mode), D, $addr>
378 where
379 Self: 'a,
380 D: 'a;
381
382 fn get_accessor<'a>(spmi: &'a D) -> Self::Accessor<'a> {
383 $crate::_Register::new(spmi)
384 }
385 }
386
387 pub type Register<'a, D = $crate::DeviceType> =
389 $crate::_Register<'a, Value, spmi_register!(@mode_type $mode), D, $addr>;
390 }
391 };
392 (
394 $name:ident, u8, $addr:expr, $mode:ident, {
395 $($tail:tt)*
396 }
397 ) => {
398 spmi_register!($name, u8, $addr, $mode, LE, { $($tail)* });
399 };
400 (@is_big BE) => { true };
401 (@is_big LE) => { false };
402}
403
404#[macro_export]
406#[doc(hidden)]
407macro_rules! spmi_register_extract_enums {
408 (
410 $value_type:ty,
411 $(#[$attr:meta])*
412 $vis:vis enum $enum_name:ident {
413 $( $variant_name:ident = $variant_val:expr ),* $(,)?
414 },
415 $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
416 $($tail:tt)*
417 ) => {
418 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
419 #[repr($value_type)]
420 $vis enum $enum_name {
421 $( $variant_name = $variant_val ),*
422 }
423 $crate::spmi_register_extract_enums!($value_type, $($tail)*);
424 };
425
426 (
428 $value_type:ty,
429 $(#[$attr:meta])*
430 $vis:vis $field:ident $(, $setter:ident)? : $bit:expr;
431 $($tail:tt)*
432 ) => {
433 $crate::spmi_register_extract_enums!($value_type, $($tail)*);
434 };
435 (
436 $value_type:ty,
437 $(#[$attr:meta])*
438 $vis:vis $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
439 $($tail:tt)*
440 ) => {
441 $crate::spmi_register_extract_enums!($value_type, $($tail)*);
442 };
443 (
444 $value_type:ty,
445 $(#[$attr:meta])*
446 $vis:vis enum $enum_type:ty,
447 $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
448 $($tail:tt)*
449 ) => {
450 $crate::spmi_register_extract_enums!($value_type, $($tail)*);
451 };
452 (
453 $value_type:ty,
454 pub const $name:ident : $type:ty = $val:expr;
455 $($tail:tt)*
456 ) => {
457 $crate::spmi_register_extract_enums!($value_type, $($tail)*);
458 };
459 ($value_type:ty) => {};
460 ($value_type:ty,) => {};
461}
462
463#[macro_export]
465#[doc(hidden)]
466macro_rules! spmi_register_fields {
467 ($value_type:ty) => {};
469 ($value_type:ty,) => {};
470
471 (
473 $value_type:ty,
474 $(#[$attr:meta])*
475 $vis:vis $field:ident $(, $setter:ident)? : $bit:expr;
476 $($tail:tt)*
477 ) => {
478 $(#[$attr])*
479 #[allow(non_snake_case)]
480 #[allow(dead_code)]
481 $vis const fn $field(&self) -> bool {
482 const _: () = assert!(
483 $bit < <$value_type>::BITS as u8,
484 "Bit index out of bounds"
485 );
486 let bit_mask = (1 as $value_type) << $bit;
487 (self.reg_value() & bit_mask) != 0
488 }
489 $(
490 #[allow(non_snake_case)]
491 #[allow(dead_code)]
492 $vis const fn $setter(self, val: bool) -> Self {
493 const _: () = assert!(
494 $bit < <$value_type>::BITS as u8,
495 "Bit index out of bounds"
496 );
497 let bit_mask = (1 as $value_type) << $bit;
498 let raw = (self.reg_value() & !bit_mask)
499 | ((val as $value_type) << $bit);
500 Self::new(raw)
501 }
502 )?
503 $crate::spmi_register_fields!($value_type, $($tail)*);
504 };
505
506 (
508 $value_type:ty,
509 $(#[$attr:meta])*
510 $vis:vis $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
511 $($tail:tt)*
512 ) => {
513 $(#[$attr])*
514 #[allow(non_snake_case)]
515 #[allow(dead_code)]
516 $vis const fn $field(&self) -> $value_type {
517 const _: () = assert!(
518 $msb < <$value_type>::BITS as u8,
519 "MSB index out of bounds"
520 );
521 const _: () = assert!(
522 $lsb < $msb,
523 "LSB must be strictly less than MSB. \
524 Use single-bit syntax (e.g., 'field: bit;') for 1-bit fields."
525 );
526 let bit_count = $msb - $lsb + 1;
527 let bit_mask = ((!0 as $value_type)
528 >> (<$value_type>::BITS as u8 - bit_count))
529 << $lsb;
530 (self.reg_value() & bit_mask) >> $lsb
531 }
532 $(
533 #[allow(non_snake_case)]
534 #[allow(dead_code)]
535 $vis const fn $setter(self, val: $value_type) -> Self {
536 const _: () = assert!(
537 $msb < <$value_type>::BITS as u8,
538 "MSB index out of bounds"
539 );
540 const _: () = assert!(
541 $lsb < $msb,
542 "LSB must be strictly less than MSB. \
543 Use single-bit syntax (e.g., 'field: bit;') \
544 for 1-bit fields."
545 );
546 let bit_count = $msb - $lsb + 1;
547 let bit_mask = ((!0 as $value_type)
548 >> (<$value_type>::BITS as u8 - bit_count))
549 << $lsb;
550 let raw = (self.reg_value() & !bit_mask)
551 | ((val << $lsb) & bit_mask);
552 Self::new(raw)
553 }
554 )?
555 $crate::spmi_register_fields!($value_type, $($tail)*);
556 };
557
558 (
560 $value_type:ty,
561 $(#[$attr:meta])*
562 $vis:vis enum $enum_type:ty,
563 $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
564 $($tail:tt)*
565 ) => {
566 $(#[$attr])*
567 #[allow(non_snake_case)]
568 #[allow(dead_code)]
569 $vis const fn $field(&self) -> $enum_type {
570 const _: () = assert!(
571 $msb < <$value_type>::BITS as u8,
572 "MSB index out of bounds"
573 );
574 const _: () = assert!(
575 $lsb <= $msb,
576 "LSB must be less than or equal to MSB"
577 );
578 let bit_count = $msb - $lsb + 1;
579 let bit_mask = ((!0 as $value_type)
580 >> (<$value_type>::BITS as u8 - bit_count))
581 << $lsb;
582 <$enum_type>::from_val((self.reg_value() & bit_mask) >> $lsb)
583 }
584 $(
585 #[allow(non_snake_case)]
586 #[allow(dead_code)]
587 $vis const fn $setter(self, val: $enum_type) -> Self {
588 const _: () = assert!(
589 $msb < <$value_type>::BITS as u8,
590 "MSB index out of bounds"
591 );
592 const _: () = assert!(
593 $lsb <= $msb,
594 "LSB must be less than or equal to MSB"
595 );
596 let bit_count = $msb - $lsb + 1;
597 let bit_mask = ((!0 as $value_type)
598 >> (<$value_type>::BITS as u8 - bit_count))
599 << $lsb;
600 let raw = (self.reg_value() & !bit_mask)
601 | (((val as $value_type) << $lsb) & bit_mask);
602 Self::new(raw)
603 }
604 )?
605 $crate::spmi_register_fields!($value_type, $($tail)*);
606 };
607
608 (
610 $value_type:ty,
611 $(#[$attr:meta])*
612 $vis:vis enum $enum_name:ident {
613 $( $variant_name:ident = $variant_val:expr ),* $(,)?
614 },
615 $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
616 $($tail:tt)*
617 ) => {
618 $(#[$attr])*
619 #[allow(non_snake_case)]
620 #[allow(dead_code)]
621 $vis const fn $field(&self) -> Result<$enum_name, $value_type> {
622 const _: () = assert!(
623 $msb < <$value_type>::BITS as u8,
624 "MSB index out of bounds"
625 );
626 const _: () = assert!(
627 $lsb <= $msb,
628 "LSB must be less than or equal to MSB"
629 );
630 let bit_count = $msb - $lsb + 1;
631 let bit_mask = ((!0 as $value_type)
632 >> (<$value_type>::BITS as u8 - bit_count))
633 << $lsb;
634 let val = (self.reg_value() & bit_mask) >> $lsb;
635 $(
636 if val == $enum_name::$variant_name as $value_type {
637 return Ok($enum_name::$variant_name);
638 }
639 )*
640 Err(val)
641 }
642 $(
643 #[allow(non_snake_case)]
644 #[allow(dead_code)]
645 $vis const fn $setter(self, val: $enum_name) -> Self {
646 const _: () = assert!(
647 $msb < <$value_type>::BITS as u8,
648 "MSB index out of bounds"
649 );
650 const _: () = assert!(
651 $lsb <= $msb,
652 "LSB must be less than or equal to MSB"
653 );
654 let bit_count = $msb - $lsb + 1;
655 let bit_mask = ((!0 as $value_type)
656 >> (<$value_type>::BITS as u8 - bit_count))
657 << $lsb;
658 let raw = (self.reg_value() & !bit_mask)
659 | (((val as $value_type) << $lsb) & bit_mask);
660 Self::new(raw)
661 }
662 )?
663 $crate::spmi_register_fields!($value_type, $($tail)*);
664 };
665
666 (
668 $value_type:ty,
669 pub const $name:ident : $type:ty = $val:expr;
670 $($tail:tt)*
671 ) => {
672 pub const $name: $type = $val;
673 $crate::spmi_register_fields!($value_type, $($tail)*);
674 };
675}
676
677#[macro_export]
679#[doc(hidden)]
680macro_rules! assert_contiguous {
681 ($regs:expr, $prev:ident, $curr:ident $(, $rest:ident)*) => {
682 $crate::assert_contiguous::<_, $prev::Value, $curr::Value>($regs);
683 $crate::assert_contiguous!($regs, $curr $(, $rest)*);
684 };
685 ($regs:expr, $last:ident) => {};
686}
687
688#[macro_export]
737macro_rules! spmi_register_block {
738 (
740 address_unit: $unit_type:ty,
741 $struct_vis:vis struct $name:ident {
742 $($tail:tt)*
743 }
744 ) => {
745 $crate::spmi_register_block!(@struct $struct_vis, $name, $($tail)*);
746
747 impl<D> $crate::RegisterBlock for $name<D> {
748 const ADDRESS_UNIT_BYTES: u16 = std::mem::size_of::<$unit_type>() as u16;
749 }
750 };
751
752 (
754 $struct_vis:vis struct $name:ident {
755 $($tail:tt)*
756 }
757 ) => {
758 $crate::spmi_register_block!(@struct $struct_vis, $name, $($tail)*);
759
760 impl<D> $crate::RegisterBlock for $name<D> {
761 const ADDRESS_UNIT_BYTES: u16 = 1;
762 }
763 };
764
765 (
767 @struct $struct_vis:vis, $name:ident,
768 $($tail:tt)*
769 ) => {
770 #[derive(Clone)]
771 $struct_vis struct $name<D = $crate::DeviceType> {
772 pub spmi: D,
773 }
774
775 #[allow(dead_code)]
776 impl<D: $crate::SpmiDevice> $name<D> {
777 $struct_vis fn new(spmi: D) -> Self {
778 Self { spmi }
779 }
780
781 #[doc(hidden)]
787 #[allow(dead_code)]
788 pub async fn read_bulk(
789 &self,
790 address: u16,
791 size: u32,
792 ) -> Result<Vec<u8>, $crate::Error> {
793 let data = $crate::SpmiDevice::read_reg(
794 &self.spmi,
795 address,
796 size,
797 ).await?;
798 if data.len() == size as usize {
799 Ok(data)
800 } else {
801 Err($crate::Error::SizeMismatch)
802 }
803 }
804
805 #[doc(hidden)]
812 #[allow(dead_code)]
813 pub async fn read_bulk_into(
814 &self,
815 address: u16,
816 out: &mut [u8],
817 ) -> Result<(), $crate::Error> {
818 let data = $crate::SpmiDevice::read_reg(
819 &self.spmi,
820 address,
821 out.len() as u32,
822 ).await?;
823 if data.len() == out.len() {
824 out.copy_from_slice(&data);
825 Ok(())
826 } else {
827 Err($crate::Error::SizeMismatch)
828 }
829 }
830
831 #[doc(hidden)]
837 #[allow(dead_code)]
838 pub async fn write_bulk(
839 &self,
840 address: u16,
841 data: &[u8],
842 ) -> Result<(), $crate::Error> {
843 $crate::SpmiDevice::write_reg(&self.spmi, address, data).await?;
844 Ok(())
845 }
846
847 $crate::spmi_register_block!(@fields D, $($tail)*);
848 }
849 };
850
851 (@fields $d:ident) => {};
852 (@fields $d:ident,) => {};
853
854 (@fields $d:ident, $(#[$meta:meta])* $vis:vis $field:ident => $reg_mod:ident, $($tail:tt)*) => {
856 $(#[$meta])*
857 #[allow(dead_code)]
858 $vis fn $field(&self) -> <$reg_mod::Value as $crate::SpmiRegister<$d>>::Accessor<'_> {
859 <$reg_mod::Value as $crate::SpmiRegister<$d>>::get_accessor(&self.spmi)
860 }
861 $crate::spmi_register_block!(@fields $d, $($tail)*);
862 };
863
864 (@fields $d:ident, $(#[$meta:meta])* $vis:vis $field:ident => $reg_mod:ident) => {
866 $(#[$meta])*
867 #[allow(dead_code)]
868 $vis fn $field(&self) -> <$reg_mod::Value as $crate::SpmiRegister<$d>>::Accessor<'_> {
869 <$reg_mod::Value as $crate::SpmiRegister<$d>>::get_accessor(&self.spmi)
870 }
871 };
872}
873
874#[doc(hidden)]
908#[macro_export]
909macro_rules! spmi_contiguous_layout {
910 (@last $head:ident) => {
911 ($head::ADDRESS, std::mem::size_of::<$head::Value>())
912 };
913
914 (@last $head:ident, $( $tail:ident ),+) => {
915 $crate::spmi_contiguous_layout!(@last $( $tail ),+)
916 };
917
918 (@last_val $head:ident) => {
919 $head::Value
920 };
921
922 (@last_val $head:ident, $( $tail:ident ),+) => {
923 $crate::spmi_contiguous_layout!(@last_val $( $tail ),+)
924 };
925
926 ($regs_ref:expr, $head:ident $(, $tail:ident )*) => {{
927 let unit = $crate::block_address_unit_bytes($regs_ref) as usize;
928 let base_addr = $head::ADDRESS;
929 let (last_addr, last_size) = $crate::spmi_contiguous_layout!(@last $head $(, $tail)*);
930 let last_step = (last_size + unit - 1) / unit;
931 let total_units = (last_addr - base_addr) as usize + last_step;
932 let total_bytes = total_units * unit;
933 (unit, base_addr, total_bytes)
934 }};
935}
936
937#[macro_export]
960macro_rules! spmi_read_contiguous {
961 (
962 $regs:expr,
963 $head:ident $(, $tail:ident )* $(,)?
964 ) => {
965 async {
966 #[allow(dead_code, non_camel_case_types)]
967 struct ReadAccessCheck<$head: $crate::SpmiRegisterDef, $( $tail: $crate::SpmiRegisterDef ),*>(
968 std::marker::PhantomData<($head, $( $tail ),*)>,
969 )
970 where
971 $head::Mode: $crate::Readable,
972 $( $tail::Mode: $crate::Readable, )*;
973
974 let _ = ReadAccessCheck::<$head::Value, $( $tail::Value ),*>(std::marker::PhantomData);
975
976 let regs_ref = $regs;
977 $crate::assert_contiguous!(regs_ref, $head $(, $tail)*);
978 let res: Result<
979 ($head::Value, $( $tail::Value ),*),
980 $crate::Error
981 > = async move {
982 let (unit, base_addr, total_bytes) =
983 $crate::spmi_contiguous_layout!(regs_ref, $head $(, $tail)*);
984
985 let data = regs_ref.read_bulk(
986 base_addr,
987 total_bytes as u32,
988 ).await?;
989
990 let head_offset = ($head::ADDRESS - base_addr) as usize * unit;
991 let $head = $head::Value::from_bytes(
992 &data[head_offset..head_offset + std::mem::size_of::<$head::Value>()],
993 )?;
994 $(
995 let tail_offset = ($tail::ADDRESS - base_addr) as usize * unit;
996 let $tail = $tail::Value::from_bytes(
997 &data[tail_offset..tail_offset + std::mem::size_of::<$tail::Value>()],
998 )?;
999 )*
1000
1001 Ok(($head, $( $tail ),*))
1002 }.await;
1003 res
1004 }
1005 };
1006}
1007
1008#[macro_export]
1036macro_rules! spmi_write_contiguous {
1037 (
1038 $regs:expr,
1039 $head:ident => $head_val:expr $(, $tail:ident => $tail_val:expr )* $(,)?
1040 ) => {
1041 async {
1042 #[allow(dead_code, non_camel_case_types)]
1043 struct WriteAccessCheck<$head: $crate::SpmiRegisterDef, $( $tail: $crate::SpmiRegisterDef ),*>(
1044 std::marker::PhantomData<($head, $( $tail ),*)>,
1045 )
1046 where
1047 $head::Mode: $crate::Writable,
1048 $( $tail::Mode: $crate::Writable, )*;
1049
1050 let _ = WriteAccessCheck::<$head::Value, $( $tail::Value ),*>(std::marker::PhantomData);
1051
1052 let regs_ref = $regs;
1053 $crate::assert_contiguous!(regs_ref, $head $(, $tail)*);
1054 let res: Result<(), $crate::Error> = async move {
1055 let (unit, base_addr, total_bytes) =
1056 $crate::spmi_contiguous_layout!(regs_ref, $head $(, $tail)*);
1057
1058 let mut bytes = vec![0u8; total_bytes];
1059
1060 let head_offset = ($head::ADDRESS - base_addr) as usize * unit;
1061 bytes[head_offset..head_offset + std::mem::size_of::<$head::Value>()]
1062 .copy_from_slice(&$head_val.to_bytes());
1063 $(
1064 let tail_offset = ($tail::ADDRESS - base_addr) as usize * unit;
1065 bytes[tail_offset..tail_offset + std::mem::size_of::<$tail::Value>()]
1066 .copy_from_slice(&$tail_val.to_bytes());
1067 )*
1068
1069 regs_ref.write_bulk(base_addr, &bytes).await?;
1070
1071 Ok(())
1072 }.await;
1073 res
1074 }
1075 };
1076}
1077
1078#[allow(async_fn_in_trait)]
1087pub trait SpmiDevice {
1088 async fn read_reg(&self, address: u16, size: u32) -> Result<Vec<u8>, crate::Error>;
1100
1101 async fn write_reg(&self, address: u16, data: &[u8]) -> Result<(), crate::Error>;
1112}
1113
1114#[cfg(test)]
1115pub struct TestSpmiDevice {
1118 registers: std::sync::Mutex<std::collections::HashMap<u16, u8>>,
1119}
1120
1121#[cfg(test)]
1122impl TestSpmiDevice {
1123 pub fn new() -> Self {
1125 Self { registers: std::sync::Mutex::new(std::collections::HashMap::new()) }
1126 }
1127}
1128
1129#[cfg(test)]
1130impl SpmiDevice for TestSpmiDevice {
1131 async fn read_reg(&self, address: u16, size: u32) -> Result<Vec<u8>, crate::Error> {
1132 let regs = self.registers.lock().unwrap();
1133 let mut data = Vec::new();
1134 for i in 0..size {
1135 let addr = address + i as u16;
1136 let val = regs.get(&addr).copied().unwrap_or(0);
1137 data.push(val);
1138 }
1139 Ok(data)
1140 }
1141
1142 async fn write_reg(&self, address: u16, data: &[u8]) -> Result<(), crate::Error> {
1143 let mut regs = self.registers.lock().unwrap();
1144 for (i, &val) in data.iter().enumerate() {
1145 let addr = address + i as u16;
1146 regs.insert(addr, val);
1147 }
1148 Ok(())
1149 }
1150}
1151
1152#[cfg(test)]
1153mod tests {
1154 use super::*;
1155
1156 spmi_register! {
1159 test_u8_reg, u8, 0xCD, RW, {
1160 pub flag, set_flag: 4;
1161 pub field, set_field: 3, 0;
1162 }
1163 }
1164
1165 spmi_register_block! {
1166 pub struct MockU8Regs {
1167 pub test => test_u8_reg,
1168 }
1169 }
1170
1171 spmi_register! {
1172 test_u16_from_bytes_reg, u16, 0x99, RW, LE, {
1173 pub field, set_field: 15, 0;
1174 }
1175 }
1176
1177 spmi_register_block! {
1178 pub struct MockU16FromBytesRegs {
1179 pub test => test_u16_from_bytes_reg,
1180 }
1181 }
1182
1183 spmi_register! {
1184 test_u16_contig_reg, u16, 0xCE, RW, LE, {
1185 pub field, set_field: 15, 0;
1186 }
1187 }
1188
1189 #[fuchsia::test]
1190 async fn test_u16_from_bytes() {
1191 let device = TestSpmiDevice::new();
1192 device.write_reg(0x99, &[0x34, 0x12]).await.unwrap();
1193
1194 let regs = MockU16FromBytesRegs::new(device);
1195 let val = regs.test().read().await.unwrap();
1196 assert_eq!(val.reg_value(), 0x1234);
1197 }
1198
1199 #[fuchsia::test]
1200 async fn test_u8_register() {
1201 let device = TestSpmiDevice::new();
1202 device.write_reg(0xCD, &[0x1A]).await.unwrap();
1203
1204 let regs = MockU8Regs::new(device);
1205 let val = regs.test().read().await.unwrap();
1206 assert_eq!(val.reg_value(), 0x1A);
1207 assert_eq!(val.flag(), true);
1208 assert_eq!(val.field(), 0x0A);
1209 }
1210
1211 spmi_register! {
1212 test_reg, u16, 0xAB, RW, LE, {
1213 pub test_bit, set_test_bit: 7;
1214 pub test_field, set_test_field: 3, 0;
1215 }
1216 }
1217
1218 spmi_register_block! {
1219 pub struct MockRegs {
1220 pub test => test_reg,
1221 }
1222 }
1223
1224 #[fuchsia::test]
1225 async fn test_read() {
1226 let device = TestSpmiDevice::new();
1227 device.write_reg(0xAB, &[0x8A, 0x00]).await.unwrap();
1228
1229 let regs = MockRegs::new(device);
1230 let val = regs.test().read().await.unwrap();
1231 assert_eq!(val.reg_value(), 0x008A);
1232 assert_eq!(val.test_bit(), true);
1233 assert_eq!(val.test_field(), 0x0A);
1234 }
1235
1236 #[fuchsia::test]
1237 async fn test_write() {
1238 let device = TestSpmiDevice::new();
1239
1240 let regs = MockRegs::new(device);
1241 let v = test_reg::Value::new(0x008A);
1242 regs.test().write(v).await.unwrap();
1243
1244 let data = regs.spmi.read_reg(0xAB, 2).await.unwrap();
1245 assert_eq!(data, &[0x8A, 0x00]);
1246 }
1247
1248 struct MockWrapper {
1249 device: TestSpmiDevice,
1250 }
1251
1252 impl SpmiDevice for MockWrapper {
1253 async fn read_reg(&self, address: u16, size: u32) -> Result<Vec<u8>, crate::Error> {
1254 self.device.read_reg(address, size).await
1255 }
1256 async fn write_reg(&self, address: u16, data: &[u8]) -> Result<(), crate::Error> {
1257 self.device.write_reg(address, data).await
1258 }
1259 }
1260
1261 spmi_register_block! {
1262 pub struct MockWrappedRegs {
1263 pub test => test_reg,
1264 }
1265 }
1266
1267 #[fuchsia::test]
1268 async fn test_wrapped_read() {
1269 let device = TestSpmiDevice::new();
1270 device.write_reg(0xAB, &[0x8A, 0x00]).await.unwrap();
1271
1272 let wrapper = MockWrapper { device };
1273 let regs = MockWrappedRegs::new(wrapper);
1274 let val = regs.test().read().await.unwrap();
1275 assert_eq!(val.reg_value(), 0x008A);
1276 assert_eq!(val.test_bit(), true);
1277 }
1278
1279 spmi_register! {
1280 test_be_reg, u16, 0xEF, RW, BE, {
1281 pub flag, set_flag: 4;
1282 pub field, set_field: 3, 0;
1283 }
1284 }
1285
1286 spmi_register_block! {
1287 pub struct MockBERegs {
1288 pub test => test_be_reg,
1289 }
1290 }
1291
1292 #[fuchsia::test]
1293 async fn test_be_register() {
1294 let device = TestSpmiDevice::new();
1295 device.write_reg(0xEF, &[0x00, 0x8A]).await.unwrap();
1296
1297 let regs = MockBERegs::new(device);
1298 let val = regs.test().read().await.unwrap();
1299 assert_eq!(val.reg_value(), 0x8A);
1300 }
1301
1302 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
1303 #[repr(u16)]
1304 pub enum PowerMode {
1305 Normal = 0,
1306 Hibernate = 1,
1307 LowPower = 2,
1308 Unknown = 0xFFFF,
1309 }
1310
1311 impl PowerMode {
1312 pub const fn from_val(val: u16) -> Self {
1313 match val {
1314 0 => PowerMode::Normal,
1315 1 => PowerMode::Hibernate,
1316 2 => PowerMode::LowPower,
1317 _ => PowerMode::Unknown,
1318 }
1319 }
1320 }
1321
1322 spmi_register! {
1323 test_enum_reg, u16, 0x44, RW, LE, {
1324 pub enum PowerMode, mode, set_mode: 3, 2;
1325 }
1326 }
1327
1328 spmi_register_block! {
1329 pub struct MockEnumRegs {
1330 pub test => test_enum_reg,
1331 }
1332 }
1333
1334 #[fuchsia::test]
1335 async fn test_enum_register() {
1336 let device = TestSpmiDevice::new();
1337
1338 let regs = MockEnumRegs::new(device);
1339 let v = test_enum_reg::Value::new(0).set_mode(PowerMode::Hibernate);
1340 regs.test().write(v).await.unwrap();
1341
1342 let data = regs.spmi.read_reg(0x44, 2).await.unwrap();
1343 assert_eq!(data, &[0x04, 0x00]);
1344 }
1345
1346 spmi_register! {
1347 test_inline_enum_reg, u16, 0x55, RW, LE, {
1348 pub enum InlineMode {
1349 A = 0,
1350 B = 1,
1351 }, mode, set_mode: 1, 0;
1352 }
1353 }
1354
1355 spmi_register_block! {
1356 pub struct MockInlineEnumRegs {
1357 pub test => test_inline_enum_reg,
1358 }
1359 }
1360
1361 #[fuchsia::test]
1362 async fn test_inline_enum() {
1363 let device = TestSpmiDevice::new();
1364
1365 let regs = MockInlineEnumRegs::new(device);
1366 let v = test_inline_enum_reg::Value::new(1).set_mode(test_inline_enum_reg::InlineMode::B);
1367 assert_eq!(v.mode(), Ok(test_inline_enum_reg::InlineMode::B));
1368 regs.test().write(v).await.unwrap();
1369
1370 let data = regs.spmi.read_reg(0x55, 2).await.unwrap();
1371 assert_eq!(data, &[0x01, 0x00]);
1372 }
1373
1374 #[fuchsia::test]
1375 async fn test_contiguous_read_write() {
1376 let device = TestSpmiDevice::new();
1377 device.write_reg(0xCD, &[0x1A, 0x34, 0x12]).await.unwrap();
1378
1379 spmi_register_block! {
1380 pub struct ContiguousRegs {
1381 pub r1 => test_u8_reg,
1382 pub r2 => test_u16_contig_reg,
1383 }
1384 }
1385 let regs = ContiguousRegs::new(device);
1386
1387 let (val_1, val_2) =
1388 spmi_read_contiguous!(®s, test_u8_reg, test_u16_contig_reg,).await.unwrap();
1389
1390 assert_eq!(val_1.reg_value(), 0x1A);
1391 assert_eq!(val_2.reg_value(), 0x1234);
1392
1393 spmi_write_contiguous!(
1394 ®s,
1395 test_u8_reg => val_1,
1396 test_u16_contig_reg => val_2
1397 )
1398 .await
1399 .unwrap();
1400
1401 let data = regs.spmi.read_reg(0xCD, 3).await.unwrap();
1402 assert_eq!(data, &[0x1A, 0x34, 0x12]);
1403 }
1404
1405 spmi_register! {
1406 test_word_reg_1, u16, 0x10, RW, LE, {}
1407 }
1408 spmi_register! {
1409 test_word_reg_2, u16, 0x11, RW, LE, {}
1410 }
1411 spmi_register! {
1412 test_word_reg_3, u16, 0x12, RW, LE, {}
1413 }
1414
1415 spmi_register_block! {
1416 address_unit: u16,
1417 pub struct WordAddressedRegs {
1418 pub r1 => test_word_reg_1,
1419 pub r2 => test_word_reg_2,
1420 pub r3 => test_word_reg_3,
1421 }
1422 }
1423
1424 #[fuchsia::test]
1425 async fn test_word_addressed_contiguous() {
1426 let device = TestSpmiDevice::new();
1427 device.write_reg(0x10, &[0x34, 0x12, 0x78, 0x56]).await.unwrap();
1428
1429 let regs = WordAddressedRegs::new(device);
1430
1431 let (val_1, val_2) =
1438 spmi_read_contiguous!(®s, test_word_reg_1, test_word_reg_2).await.unwrap();
1439 assert_eq!(val_1.reg_value(), 0x1234);
1440 assert_eq!(val_2.reg_value(), 0x5678);
1441
1442 spmi_write_contiguous!(
1444 ®s,
1445 test_word_reg_1 => val_1,
1446 test_word_reg_2 => val_2
1447 )
1448 .await
1449 .unwrap();
1450
1451 let data = regs.spmi.read_reg(0x10, 4).await.unwrap();
1452 assert_eq!(data, &[0x34, 0x12, 0x78, 0x56]);
1453 }
1454
1455 #[fuchsia::test]
1456 async fn test_read_write_bulk() {
1457 let device = TestSpmiDevice::new();
1458 device.write_reg(0x88, &[0x1A, 0x2B, 0x3C]).await.unwrap();
1459
1460 let regs = MockU8Regs::new(device);
1461 let bytes = regs.read_bulk(0x88, 3).await.unwrap();
1462 assert_eq!(bytes, vec![0x1A, 0x2B, 0x3C]);
1463
1464 let mut buf = [0u8; 3];
1465 regs.read_bulk_into(0x88, &mut buf).await.unwrap();
1466 assert_eq!(buf, [0x1A, 0x2B, 0x3C]);
1467
1468 regs.write_bulk(0x88, &[0x1A, 0x2B, 0x3C]).await.unwrap();
1469 let data = regs.spmi.read_reg(0x88, 3).await.unwrap();
1470 assert_eq!(data, &[0x1A, 0x2B, 0x3C]);
1471 }
1472
1473 spmi_register! {
1474 test_mixed_u8, u8, 0x20, RW, {}
1475 }
1476 spmi_register! {
1477 test_mixed_u16, u16, 0x21, RW, LE, {}
1478 }
1479
1480 spmi_register_block! {
1481 address_unit: u16,
1482 pub struct WordAddressedMixedRegs {
1483 pub u8_reg => test_mixed_u8,
1484 pub u16_reg => test_mixed_u16,
1485 }
1486 }
1487
1488 #[fuchsia::test]
1489 async fn test_word_addressed_mixed_sizes_contiguous() {
1490 let device = TestSpmiDevice::new();
1491 device.write_reg(0x20, &[0xAB, 0x00, 0x34, 0x12]).await.unwrap();
1493
1494 let regs = WordAddressedMixedRegs::new(device);
1495 let (val_u8, val_u16) =
1496 spmi_read_contiguous!(®s, test_mixed_u8, test_mixed_u16).await.unwrap();
1497
1498 assert_eq!(val_u8.reg_value(), 0xAB);
1499 assert_eq!(val_u16.reg_value(), 0x1234);
1500
1501 spmi_write_contiguous!(
1502 ®s,
1503 test_mixed_u8 => val_u8,
1504 test_mixed_u16 => val_u16
1505 )
1506 .await
1507 .unwrap();
1508
1509 let data = regs.spmi.read_reg(0x20, 4).await.unwrap();
1510 assert_eq!(data, &[0xAB, 0x00, 0x34, 0x12]);
1511 }
1512
1513 #[derive(
1514 Copy,
1515 Clone,
1516 Debug,
1517 PartialEq,
1518 Eq,
1519 zerocopy::FromBytes,
1520 zerocopy::IntoBytes,
1521 zerocopy::KnownLayout,
1522 zerocopy::Immutable,
1523 )]
1524 #[repr(C)]
1525 pub struct Reg3Byte([u8; 3]);
1526 impl RegisterValue for Reg3Byte {}
1527
1528 impl Reg3Byte {
1529 fn from_bytes(bytes: &[u8]) -> Result<Self, crate::Error> {
1530 let slice: [u8; 3] = bytes.try_into().unwrap();
1531 Ok(Self(slice))
1532 }
1533 fn to_bytes(&self) -> [u8; 3] {
1534 self.0
1535 }
1536 fn reg_value(&self) -> Self {
1537 *self
1538 }
1539 }
1540
1541 mod test_3byte_reg {
1542 use super::*;
1543 pub type Value = Reg3Byte;
1544 pub const ADDRESS: u16 = 0x30;
1545 }
1546
1547 impl SpmiRegisterDef for Reg3Byte {
1548 type Value = Self;
1549 type Mode = crate::ReadWrite;
1550 const ADDRESS: u16 = 0x30;
1551 }
1552
1553 impl<D> SpmiRegister<D> for Reg3Byte {
1554 type Accessor<'a>
1555 = &'a D
1556 where
1557 Self: 'a,
1558 D: 'a;
1559 fn get_accessor<'a>(spmi: &'a D) -> Self::Accessor<'a> {
1560 spmi
1561 }
1562 }
1563 spmi_register! {
1564 test_after_3byte_reg, u16, 0x32, RW, LE, {}
1565 }
1566
1567 spmi_register_block! {
1568 address_unit: u16,
1569 pub struct WordAddressed3ByteRegs {
1570 pub r3b => test_3byte_reg,
1571 pub r_next => test_after_3byte_reg,
1572 }
1573 }
1574
1575 #[fuchsia::test]
1576 async fn test_word_addressed_non_multiple_size_contiguous() {
1577 let device = TestSpmiDevice::new();
1578 device.write_reg(0x30, &[0x11, 0x22, 0x33, 0x00, 0x56, 0x78]).await.unwrap();
1581
1582 let regs = WordAddressed3ByteRegs::new(device);
1583 let (val_3b, val_next) =
1584 spmi_read_contiguous!(®s, test_3byte_reg, test_after_3byte_reg).await.unwrap();
1585
1586 assert_eq!(val_3b.reg_value(), Reg3Byte([0x11, 0x22, 0x33]));
1587 assert_eq!(val_next.reg_value(), 0x7856);
1588
1589 spmi_write_contiguous!(
1590 ®s,
1591 test_3byte_reg => val_3b,
1592 test_after_3byte_reg => val_next
1593 )
1594 .await
1595 .unwrap();
1596
1597 let data = regs.spmi.read_reg(0x30, 6).await.unwrap();
1598 assert_eq!(data, &[0x11, 0x22, 0x33, 0x00, 0x56, 0x78]);
1599 }
1600}