1use zerocopy::{FromBytes, FromZeros, Immutable, IntoBytes};
6
7pub trait RegisterValue: Sized + FromBytes + IntoBytes + FromZeros + Immutable {}
13
14pub struct ReadOnly;
21
22pub struct WriteOnly;
24
25pub struct ReadWrite;
27
28pub trait Readable {}
33impl Readable for ReadOnly {}
34impl Readable for ReadWrite {}
35
36pub trait Writable {}
41impl Writable for WriteOnly {}
42impl Writable for ReadWrite {}
43
44pub struct _Register<'a, T, M, D, const ADDR: u16> {
53 pub spmi: &'a D,
55 _marker: std::marker::PhantomData<(T, M)>,
56}
57
58impl<'a, T, M, D, const ADDR: u16> _Register<'a, T, M, D, ADDR> {
59 pub fn new(spmi: &'a D) -> Self {
61 Self { spmi, _marker: std::marker::PhantomData }
62 }
63}
64
65impl<'a, T, M, D, const ADDR: u16> _Register<'a, T, M, D, ADDR>
67where
68 M: Readable,
69 T: RegisterValue,
70 D: SpmiDevice,
71{
72 pub async fn read(&self) -> Result<T, crate::Error> {
77 let size = std::mem::size_of::<T>();
78 let bytes = self.spmi.read_reg(ADDR, size as u32).await?;
79 T::read_from_bytes(&bytes).map_err(|_| crate::Error::SizeMismatch)
80 }
81}
82
83impl<'a, T, M, D, const ADDR: u16> _Register<'a, T, M, D, ADDR>
85where
86 M: Writable,
87 T: RegisterValue,
88 D: SpmiDevice,
89{
90 pub async fn write(&self, val: T) -> Result<(), crate::Error> {
95 let bytes = val.as_bytes();
96 self.spmi.write_reg(ADDR, bytes).await?;
97 Ok(())
98 }
99}
100
101#[macro_export]
202macro_rules! spmi_register {
203 (@map_reg RO, $val:ty, $addr:expr, BE) => {
205 $crate::Register<'a, $val, $crate::ReadOnly, $addr>
206 };
207 (@map_reg RO, $val:ty, $addr:expr, LE) => {
208 $crate::Register<'a, $val, $crate::ReadOnly, $addr>
209 };
210 (@map_reg WO, $val:ty, $addr:expr, BE) => {
211 $crate::Register<'a, $val, $crate::WriteOnly, $addr>
212 };
213 (@map_reg WO, $val:ty, $addr:expr, LE) => {
214 $crate::Register<'a, $val, $crate::WriteOnly, $addr>
215 };
216 (@map_reg RW, $val:ty, $addr:expr, BE) => {
217 $crate::Register<'a, $val, $crate::ReadWrite, $addr>
218 };
219 (@map_reg RW, $val:ty, $addr:expr, LE) => {
220 $crate::Register<'a, $val, $crate::ReadWrite, $addr>
221 };
222
223 (@byteorder_type u8, $endianness:ident) => { u8 };
225 (@byteorder_type u16, LE) => { $crate::U16<$crate::LittleEndian> };
226 (@byteorder_type u16, BE) => { $crate::U16<$crate::BigEndian> };
227 (@byteorder_type u32, LE) => { $crate::U32<$crate::LittleEndian> };
228 (@byteorder_type u32, BE) => { $crate::U32<$crate::BigEndian> };
229
230 (@new u8, $endianness:ident, $val:expr) => { Self($val) };
231 (@new u16, $endianness:ident, $val:expr) => {
232 Self($crate::U16::new($val))
233 };
234 (@new u32, $endianness:ident, $val:expr) => {
235 Self($crate::U32::new($val))
236 };
237
238 (@get u8, $endianness:ident, $val:expr) => { $val };
239 (@get u16, $endianness:ident, $val:expr) => { $val.get() };
240 (@get u32, $endianness:ident, $val:expr) => { $val.get() };
241
242 (
244 $name:ident,
245 $value_type:ident,
246 $addr:expr,
247 $mode:ident,
248 $endianness:ident,
249 {
250 $($tail:tt)*
251 }
252 ) => {
253 #[allow(unused_imports)]
254 #[allow(dead_code)]
255 pub mod $name {
256 use super::*;
257 use $crate::zerocopy_reexport as zerocopy;
258
259 pub const ADDRESS: u16 = $addr;
261
262 $crate::spmi_register_extract_enums!($value_type, $($tail)*);
263
264 #[derive(
266 Copy,
267 Clone,
268 Debug,
269 PartialEq,
270 Eq,
271 zerocopy::FromBytes,
272 zerocopy::IntoBytes,
273 zerocopy::Immutable,
274 )]
275 #[repr(transparent)]
276 pub struct Value(
277 pub spmi_register!(@byteorder_type $value_type, $endianness),
278 );
279
280 impl Value {
281 pub const fn new(val: $value_type) -> Self {
282 spmi_register!(@new $value_type, $endianness, val)
283 }
284 pub const fn reg_value(&self) -> $value_type {
285 spmi_register!(@get $value_type, $endianness, self.0)
286 }
287
288 pub fn from_bytes(bytes: &[u8]) -> Result<Self, $crate::Error> {
290 zerocopy::FromBytes::read_from_bytes(bytes)
291 .map_err(|_| $crate::Error::SizeMismatch)
292 }
293
294 pub fn to_bytes(
296 &self,
297 ) -> [u8; std::mem::size_of::<$value_type>()] {
298 let mut arr = [0u8; std::mem::size_of::<$value_type>()];
299 arr.copy_from_slice($crate::IntoBytes::as_bytes(self));
300 arr
301 }
302
303 $crate::spmi_register_fields!($value_type, $($tail)*);
304 }
305
306 impl $crate::RegisterValue for Value {}
307
308 impl Default for Value {
309 fn default() -> Self {
310 Self::new(0 as $value_type)
311 }
312 }
313
314 pub type Register<'a> =
316 spmi_register!(@map_reg $mode, Value, $addr, $endianness);
317 }
318 };
319 (
321 $name:ident, u8, $addr:expr, $mode:ident, {
322 $($tail:tt)*
323 }
324 ) => {
325 spmi_register!($name, u8, $addr, $mode, LE, { $($tail)* });
326 };
327 (@is_big BE) => { true };
328 (@is_big LE) => { false };
329}
330
331#[macro_export]
333#[doc(hidden)]
334macro_rules! spmi_register_extract_enums {
335 (
337 $value_type:ty,
338 $(#[$attr:meta])*
339 $vis:vis enum $enum_name:ident {
340 $( $variant_name:ident = $variant_val:expr ),* $(,)?
341 },
342 $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
343 $($tail:tt)*
344 ) => {
345 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
346 #[repr($value_type)]
347 $vis enum $enum_name {
348 $( $variant_name = $variant_val ),*
349 }
350 $crate::spmi_register_extract_enums!($value_type, $($tail)*);
351 };
352
353 (
355 $value_type:ty,
356 $(#[$attr:meta])*
357 $vis:vis $field:ident $(, $setter:ident)? : $bit:expr;
358 $($tail:tt)*
359 ) => {
360 $crate::spmi_register_extract_enums!($value_type, $($tail)*);
361 };
362 (
363 $value_type:ty,
364 $(#[$attr:meta])*
365 $vis:vis $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
366 $($tail:tt)*
367 ) => {
368 $crate::spmi_register_extract_enums!($value_type, $($tail)*);
369 };
370 (
371 $value_type:ty,
372 $(#[$attr:meta])*
373 $vis:vis enum $enum_type:ty,
374 $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
375 $($tail:tt)*
376 ) => {
377 $crate::spmi_register_extract_enums!($value_type, $($tail)*);
378 };
379 (
380 $value_type:ty,
381 pub const $name:ident : $type:ty = $val:expr;
382 $($tail:tt)*
383 ) => {
384 $crate::spmi_register_extract_enums!($value_type, $($tail)*);
385 };
386 ($value_type:ty) => {};
387 ($value_type:ty,) => {};
388}
389
390#[macro_export]
392#[doc(hidden)]
393macro_rules! spmi_register_fields {
394 ($value_type:ty) => {};
396 ($value_type:ty,) => {};
397
398 (
400 $value_type:ty,
401 $(#[$attr:meta])*
402 $vis:vis $field:ident $(, $setter:ident)? : $bit:expr;
403 $($tail:tt)*
404 ) => {
405 $(#[$attr])*
406 #[allow(non_snake_case)]
407 #[allow(dead_code)]
408 $vis const fn $field(&self) -> bool {
409 const _: () = assert!(
410 $bit < <$value_type>::BITS as u8,
411 "Bit index out of bounds"
412 );
413 let bit_mask = (1 as $value_type) << $bit;
414 (self.reg_value() & bit_mask) != 0
415 }
416 $(
417 #[allow(non_snake_case)]
418 #[allow(dead_code)]
419 $vis const fn $setter(self, val: bool) -> Self {
420 const _: () = assert!(
421 $bit < <$value_type>::BITS as u8,
422 "Bit index out of bounds"
423 );
424 let bit_mask = (1 as $value_type) << $bit;
425 let raw = (self.reg_value() & !bit_mask)
426 | ((val as $value_type) << $bit);
427 Self::new(raw)
428 }
429 )?
430 $crate::spmi_register_fields!($value_type, $($tail)*);
431 };
432
433 (
435 $value_type:ty,
436 $(#[$attr:meta])*
437 $vis:vis $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
438 $($tail:tt)*
439 ) => {
440 $(#[$attr])*
441 #[allow(non_snake_case)]
442 #[allow(dead_code)]
443 $vis const fn $field(&self) -> $value_type {
444 const _: () = assert!(
445 $msb < <$value_type>::BITS as u8,
446 "MSB index out of bounds"
447 );
448 const _: () = assert!(
449 $lsb < $msb,
450 "LSB must be strictly less than MSB. \
451 Use single-bit syntax (e.g., 'field: bit;') for 1-bit fields."
452 );
453 let bit_count = $msb - $lsb + 1;
454 let bit_mask = ((!0 as $value_type)
455 >> (<$value_type>::BITS as u8 - bit_count))
456 << $lsb;
457 (self.reg_value() & bit_mask) >> $lsb
458 }
459 $(
460 #[allow(non_snake_case)]
461 #[allow(dead_code)]
462 $vis const fn $setter(self, val: $value_type) -> Self {
463 const _: () = assert!(
464 $msb < <$value_type>::BITS as u8,
465 "MSB index out of bounds"
466 );
467 const _: () = assert!(
468 $lsb < $msb,
469 "LSB must be strictly less than MSB. \
470 Use single-bit syntax (e.g., 'field: bit;') \
471 for 1-bit fields."
472 );
473 let bit_count = $msb - $lsb + 1;
474 let bit_mask = ((!0 as $value_type)
475 >> (<$value_type>::BITS as u8 - bit_count))
476 << $lsb;
477 let raw = (self.reg_value() & !bit_mask)
478 | ((val << $lsb) & bit_mask);
479 Self::new(raw)
480 }
481 )?
482 $crate::spmi_register_fields!($value_type, $($tail)*);
483 };
484
485 (
487 $value_type:ty,
488 $(#[$attr:meta])*
489 $vis:vis enum $enum_type:ty,
490 $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
491 $($tail:tt)*
492 ) => {
493 $(#[$attr])*
494 #[allow(non_snake_case)]
495 #[allow(dead_code)]
496 $vis const fn $field(&self) -> $enum_type {
497 const _: () = assert!(
498 $msb < <$value_type>::BITS as u8,
499 "MSB index out of bounds"
500 );
501 const _: () = assert!(
502 $lsb <= $msb,
503 "LSB must be less than or equal to MSB"
504 );
505 let bit_count = $msb - $lsb + 1;
506 let bit_mask = ((!0 as $value_type)
507 >> (<$value_type>::BITS as u8 - bit_count))
508 << $lsb;
509 <$enum_type>::from_val((self.reg_value() & bit_mask) >> $lsb)
510 }
511 $(
512 #[allow(non_snake_case)]
513 #[allow(dead_code)]
514 $vis const fn $setter(self, val: $enum_type) -> Self {
515 const _: () = assert!(
516 $msb < <$value_type>::BITS as u8,
517 "MSB index out of bounds"
518 );
519 const _: () = assert!(
520 $lsb <= $msb,
521 "LSB must be less than or equal to MSB"
522 );
523 let bit_count = $msb - $lsb + 1;
524 let bit_mask = ((!0 as $value_type)
525 >> (<$value_type>::BITS as u8 - bit_count))
526 << $lsb;
527 let raw = (self.reg_value() & !bit_mask)
528 | (((val as $value_type) << $lsb) & bit_mask);
529 Self::new(raw)
530 }
531 )?
532 $crate::spmi_register_fields!($value_type, $($tail)*);
533 };
534
535 (
537 $value_type:ty,
538 $(#[$attr:meta])*
539 $vis:vis enum $enum_name:ident {
540 $( $variant_name:ident = $variant_val:expr ),* $(,)?
541 },
542 $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
543 $($tail:tt)*
544 ) => {
545 $(#[$attr])*
546 #[allow(non_snake_case)]
547 #[allow(dead_code)]
548 $vis const fn $field(&self) -> Result<$enum_name, $value_type> {
549 const _: () = assert!(
550 $msb < <$value_type>::BITS as u8,
551 "MSB index out of bounds"
552 );
553 const _: () = assert!(
554 $lsb <= $msb,
555 "LSB must be less than or equal to MSB"
556 );
557 let bit_count = $msb - $lsb + 1;
558 let bit_mask = ((!0 as $value_type)
559 >> (<$value_type>::BITS as u8 - bit_count))
560 << $lsb;
561 let val = (self.reg_value() & bit_mask) >> $lsb;
562 $(
563 if val == $enum_name::$variant_name as $value_type {
564 return Ok($enum_name::$variant_name);
565 }
566 )*
567 Err(val)
568 }
569 $(
570 #[allow(non_snake_case)]
571 #[allow(dead_code)]
572 $vis const fn $setter(self, val: $enum_name) -> Self {
573 const _: () = assert!(
574 $msb < <$value_type>::BITS as u8,
575 "MSB index out of bounds"
576 );
577 const _: () = assert!(
578 $lsb <= $msb,
579 "LSB must be less than or equal to MSB"
580 );
581 let bit_count = $msb - $lsb + 1;
582 let bit_mask = ((!0 as $value_type)
583 >> (<$value_type>::BITS as u8 - bit_count))
584 << $lsb;
585 let raw = (self.reg_value() & !bit_mask)
586 | (((val as $value_type) << $lsb) & bit_mask);
587 Self::new(raw)
588 }
589 )?
590 $crate::spmi_register_fields!($value_type, $($tail)*);
591 };
592
593 (
595 $value_type:ty,
596 pub const $name:ident : $type:ty = $val:expr;
597 $($tail:tt)*
598 ) => {
599 pub const $name: $type = $val;
600 $crate::spmi_register_fields!($value_type, $($tail)*);
601 };
602}
603
604#[macro_export]
606#[doc(hidden)]
607macro_rules! assert_contiguous {
608 ($prev:ident, $curr:ident $(, $rest:ident)*) => {
609 const _: () = assert!(
610 $curr::ADDRESS == $prev::ADDRESS
611 + std::mem::size_of::<$prev::Value>() as u16,
612 concat!(
613 "Registers ",
614 stringify!($prev),
615 " and ",
616 stringify!($curr),
617 " are not contiguous"
618 )
619 );
620 $crate::assert_contiguous!($curr $(, $rest)*);
621 };
622 ($last:ident) => {};
623}
624
625#[macro_export]
672macro_rules! spmi_register_block {
673 (
674 pub struct $name:ident {
675 $($tail:tt)*
676 }
677 ) => {
678 pub struct $name {
679 pub spmi: $crate::DeviceType,
680 }
681
682 #[allow(dead_code)]
683 impl $name {
684 pub fn new(spmi: $crate::DeviceType) -> Self {
685 Self { spmi }
686 }
687
688 #[doc(hidden)]
694 #[allow(dead_code)]
695 pub async fn read_bulk(
696 &self,
697 address: u16,
698 size: u32,
699 ) -> Result<Vec<u8>, $crate::Error> {
700 let data = $crate::SpmiDevice::read_reg(
701 &self.spmi,
702 address,
703 size,
704 ).await?;
705 if data.len() == size as usize {
706 Ok(data)
707 } else {
708 Err($crate::Error::SizeMismatch)
709 }
710 }
711
712 #[doc(hidden)]
719 #[allow(dead_code)]
720 pub async fn read_bulk_into(
721 &self,
722 address: u16,
723 out: &mut [u8],
724 ) -> Result<(), $crate::Error> {
725 let data = $crate::SpmiDevice::read_reg(
726 &self.spmi,
727 address,
728 out.len() as u32,
729 ).await?;
730 if data.len() == out.len() {
731 out.copy_from_slice(&data);
732 Ok(())
733 } else {
734 Err($crate::Error::SizeMismatch)
735 }
736 }
737
738 #[doc(hidden)]
744 #[allow(dead_code)]
745 pub async fn write_bulk(
746 &self,
747 address: u16,
748 data: &[u8],
749 ) -> Result<(), $crate::Error> {
750 $crate::SpmiDevice::write_reg(&self.spmi, address, data).await?;
751 Ok(())
752 }
753
754 spmi_register_block!(@fields $($tail)*);
755 }
756 };
757
758 (@fields) => {};
759
760 (@fields $vis:vis $field:ident => $reg_mod:ident, $($tail:tt)*) => {
762 #[allow(dead_code)]
763 $vis fn $field(&self) -> $reg_mod::Register<'_> {
764 $reg_mod::Register::new(&self.spmi)
765 }
766 spmi_register_block!(@fields $($tail)*);
767 };
768
769 (@fields $vis:vis $field:ident => $reg_mod:ident) => {
771 #[allow(dead_code)]
772 $vis fn $field(&self) -> $reg_mod::Register<'_> {
773 $reg_mod::Register::new(&self.spmi)
774 }
775 };
776}
777
778#[macro_export]
811macro_rules! spmi_read_contiguous {
812 (
813 $regs:expr,
814 $head:ident $(, $tail:ident )* $(,)?
815 ) => {
816 async {
817 $crate::assert_contiguous!($head $(, $tail)*);
818 let res: Result<
819 ($head::Value, $( $tail::Value ),*),
820 $crate::Error
821 > = async {
822 let base_addr = $head::ADDRESS;
823
824 let total_size = std::mem::size_of::<$head::Value>()
825 $( + std::mem::size_of::<$tail::Value>() )*;
826
827 let data = $regs.read_bulk(
828 base_addr,
829 total_size as u32,
830 ).await?;
831
832 let mut cursor = 0;
833 let $head = $head::Value::from_bytes(
834 &data[cursor..std::mem::size_of::<$head::Value>()],
835 )?;
836 cursor = std::mem::size_of::<$head::Value>();
837 $(
838 let end = cursor + std::mem::size_of::<$tail::Value>();
839 let $tail = $tail::Value::from_bytes(
840 &data[cursor..end],
841 )?;
842 cursor = end;
843 )*
844 let _ = cursor;
845
846 Ok(($head, $( $tail ),*))
847 }.await;
848 res
849 }
850 };
851}
852
853#[macro_export]
877macro_rules! spmi_write_contiguous {
878 (
879 $regs:expr,
880 $head:ident => $head_val:expr $(, $tail:ident => $tail_val:expr )* $(,)?
881 ) => {
882 async {
883 $crate::assert_contiguous!($head $(, $tail)*);
884 let regs_ref = $regs;
885 let res: Result<(), $crate::Error> = async move {
886 let base_addr = $head::ADDRESS;
887
888 let mut bytes = Vec::new();
889 bytes.extend_from_slice(&$head_val.to_bytes());
890 $(
891 bytes.extend_from_slice(&$tail_val.to_bytes());
892 )*
893
894 regs_ref.write_bulk(base_addr, &bytes).await?;
895
896 Ok(())
897 }.await;
898 res
899 }
900 };
901}
902
903#[allow(async_fn_in_trait)]
912pub trait SpmiDevice {
913 async fn read_reg(&self, address: u16, size: u32) -> Result<Vec<u8>, crate::Error>;
925
926 async fn write_reg(&self, address: u16, data: &[u8]) -> Result<(), crate::Error>;
937}
938
939#[cfg(test)]
940pub struct TestSpmiDevice {
943 registers: std::sync::Mutex<std::collections::HashMap<u16, u8>>,
944}
945
946#[cfg(test)]
947impl TestSpmiDevice {
948 pub fn new() -> Self {
950 Self { registers: std::sync::Mutex::new(std::collections::HashMap::new()) }
951 }
952}
953
954#[cfg(test)]
955impl SpmiDevice for TestSpmiDevice {
956 async fn read_reg(&self, address: u16, size: u32) -> Result<Vec<u8>, crate::Error> {
957 let regs = self.registers.lock().unwrap();
958 let mut data = Vec::new();
959 for i in 0..size {
960 let addr = address + i as u16;
961 let val = regs.get(&addr).copied().unwrap_or(0);
962 data.push(val);
963 }
964 Ok(data)
965 }
966
967 async fn write_reg(&self, address: u16, data: &[u8]) -> Result<(), crate::Error> {
968 let mut regs = self.registers.lock().unwrap();
969 for (i, &val) in data.iter().enumerate() {
970 let addr = address + i as u16;
971 regs.insert(addr, val);
972 }
973 Ok(())
974 }
975}
976
977#[cfg(test)]
978mod tests {
979 use super::*;
980
981 spmi_register! {
984 test_u8_reg, u8, 0xCD, RW, {
985 pub flag, set_flag: 4;
986 pub field, set_field: 3, 0;
987 }
988 }
989
990 spmi_register_block! {
991 pub struct MockU8Regs {
992 pub test => test_u8_reg,
993 }
994 }
995
996 spmi_register! {
997 test_u16_from_bytes_reg, u16, 0x99, RW, LE, {
998 pub field, set_field: 15, 0;
999 }
1000 }
1001
1002 spmi_register_block! {
1003 pub struct MockU16FromBytesRegs {
1004 pub test => test_u16_from_bytes_reg,
1005 }
1006 }
1007
1008 spmi_register! {
1009 test_u16_contig_reg, u16, 0xCE, RW, LE, {
1010 pub field, set_field: 15, 0;
1011 }
1012 }
1013
1014 #[fuchsia::test]
1015 async fn test_u16_from_bytes() {
1016 let device = TestSpmiDevice::new();
1017 device.write_reg(0x99, &[0x34, 0x12]).await.unwrap();
1018
1019 let regs = MockU16FromBytesRegs::new(device);
1020 let val = regs.test().read().await.unwrap();
1021 assert_eq!(val.reg_value(), 0x1234);
1022 }
1023
1024 #[fuchsia::test]
1025 async fn test_u8_register() {
1026 let device = TestSpmiDevice::new();
1027 device.write_reg(0xCD, &[0x1A]).await.unwrap();
1028
1029 let regs = MockU8Regs::new(device);
1030 let val = regs.test().read().await.unwrap();
1031 assert_eq!(val.reg_value(), 0x1A);
1032 assert_eq!(val.flag(), true);
1033 assert_eq!(val.field(), 0x0A);
1034 }
1035
1036 spmi_register! {
1037 test_reg, u16, 0xAB, RW, LE, {
1038 pub test_bit, set_test_bit: 7;
1039 pub test_field, set_test_field: 3, 0;
1040 }
1041 }
1042
1043 spmi_register_block! {
1044 pub struct MockRegs {
1045 pub test => test_reg,
1046 }
1047 }
1048
1049 #[fuchsia::test]
1050 async fn test_read() {
1051 let device = TestSpmiDevice::new();
1052 device.write_reg(0xAB, &[0x8A, 0x00]).await.unwrap();
1053
1054 let regs = MockRegs::new(device);
1055 let val = regs.test().read().await.unwrap();
1056 assert_eq!(val.reg_value(), 0x008A);
1057 assert_eq!(val.test_bit(), true);
1058 assert_eq!(val.test_field(), 0x0A);
1059 }
1060
1061 #[fuchsia::test]
1062 async fn test_write() {
1063 let device = TestSpmiDevice::new();
1064
1065 let regs = MockRegs::new(device);
1066 let v = test_reg::Value::new(0x008A);
1067 regs.test().write(v).await.unwrap();
1068
1069 let data = regs.spmi.read_reg(0xAB, 2).await.unwrap();
1070 assert_eq!(data, &[0x8A, 0x00]);
1071 }
1072
1073 spmi_register! {
1074 test_be_reg, u16, 0xEF, RW, BE, {
1075 pub flag, set_flag: 4;
1076 pub field, set_field: 3, 0;
1077 }
1078 }
1079
1080 spmi_register_block! {
1081 pub struct MockBERegs {
1082 pub test => test_be_reg,
1083 }
1084 }
1085
1086 #[fuchsia::test]
1087 async fn test_be_register() {
1088 let device = TestSpmiDevice::new();
1089 device.write_reg(0xEF, &[0x00, 0x8A]).await.unwrap();
1090
1091 let regs = MockBERegs::new(device);
1092 let val = regs.test().read().await.unwrap();
1093 assert_eq!(val.reg_value(), 0x8A);
1094 }
1095
1096 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
1097 #[repr(u16)]
1098 pub enum PowerMode {
1099 Normal = 0,
1100 Hibernate = 1,
1101 LowPower = 2,
1102 Unknown = 0xFFFF,
1103 }
1104
1105 impl PowerMode {
1106 pub const fn from_val(val: u16) -> Self {
1107 match val {
1108 0 => PowerMode::Normal,
1109 1 => PowerMode::Hibernate,
1110 2 => PowerMode::LowPower,
1111 _ => PowerMode::Unknown,
1112 }
1113 }
1114 }
1115
1116 spmi_register! {
1117 test_enum_reg, u16, 0x44, RW, LE, {
1118 pub enum PowerMode, mode, set_mode: 3, 2;
1119 }
1120 }
1121
1122 spmi_register_block! {
1123 pub struct MockEnumRegs {
1124 pub test => test_enum_reg,
1125 }
1126 }
1127
1128 #[fuchsia::test]
1129 async fn test_enum_register() {
1130 let device = TestSpmiDevice::new();
1131
1132 let regs = MockEnumRegs::new(device);
1133 let v = test_enum_reg::Value::new(0).set_mode(PowerMode::Hibernate);
1134 regs.test().write(v).await.unwrap();
1135
1136 let data = regs.spmi.read_reg(0x44, 2).await.unwrap();
1137 assert_eq!(data, &[0x04, 0x00]);
1138 }
1139
1140 spmi_register! {
1141 test_inline_enum_reg, u16, 0x55, RW, LE, {
1142 pub enum InlineMode {
1143 A = 0,
1144 B = 1,
1145 }, mode, set_mode: 1, 0;
1146 }
1147 }
1148
1149 spmi_register_block! {
1150 pub struct MockInlineEnumRegs {
1151 pub test => test_inline_enum_reg,
1152 }
1153 }
1154
1155 #[fuchsia::test]
1156 async fn test_inline_enum() {
1157 let device = TestSpmiDevice::new();
1158
1159 let regs = MockInlineEnumRegs::new(device);
1160 let v = test_inline_enum_reg::Value::new(1).set_mode(test_inline_enum_reg::InlineMode::B);
1161 assert_eq!(v.mode(), Ok(test_inline_enum_reg::InlineMode::B));
1162 regs.test().write(v).await.unwrap();
1163
1164 let data = regs.spmi.read_reg(0x55, 2).await.unwrap();
1165 assert_eq!(data, &[0x01, 0x00]);
1166 }
1167
1168 #[fuchsia::test]
1169 async fn test_contiguous_read_write() {
1170 let device = TestSpmiDevice::new();
1171 device.write_reg(0xCD, &[0x1A, 0x34, 0x12]).await.unwrap();
1172
1173 spmi_register_block! {
1174 pub struct ContiguousRegs {
1175 pub r1 => test_u8_reg,
1176 pub r2 => test_u16_contig_reg,
1177 }
1178 }
1179 let regs = ContiguousRegs::new(device);
1180
1181 let (val_1, val_2) =
1182 spmi_read_contiguous!(®s, test_u8_reg, test_u16_contig_reg,).await.unwrap();
1183
1184 assert_eq!(val_1.reg_value(), 0x1A);
1185 assert_eq!(val_2.reg_value(), 0x1234);
1186
1187 spmi_write_contiguous!(
1188 ®s,
1189 test_u8_reg => val_1,
1190 test_u16_contig_reg => val_2
1191 )
1192 .await
1193 .unwrap();
1194
1195 let data = regs.spmi.read_reg(0xCD, 3).await.unwrap();
1196 assert_eq!(data, &[0x1A, 0x34, 0x12]);
1197 }
1198
1199 #[fuchsia::test]
1200 async fn test_read_write_bulk() {
1201 let device = TestSpmiDevice::new();
1202 device.write_reg(0x88, &[0x1A, 0x2B, 0x3C]).await.unwrap();
1203
1204 let regs = MockU8Regs::new(device);
1205 let bytes = regs.read_bulk(0x88, 3).await.unwrap();
1206 assert_eq!(bytes, vec![0x1A, 0x2B, 0x3C]);
1207
1208 let mut buf = [0u8; 3];
1209 regs.read_bulk_into(0x88, &mut buf).await.unwrap();
1210 assert_eq!(buf, [0x1A, 0x2B, 0x3C]);
1211
1212 regs.write_bulk(0x88, &[0x1A, 0x2B, 0x3C]).await.unwrap();
1213 let data = regs.spmi.read_reg(0x88, 3).await.unwrap();
1214 assert_eq!(data, &[0x1A, 0x2B, 0x3C]);
1215 }
1216}