55#ifndef SRC_DEVICES_SPMI_LIB_HWREG_SPMI_INCLUDE_HWREG_SPMI_H_
66#define SRC_DEVICES_SPMI_LIB_HWREG_SPMI_INCLUDE_HWREG_SPMI_H_
77
8+ #include < endian.h>
89#include < fidl/fuchsia.hardware.spmi/cpp/wire.h>
910#include < lib/zx/result.h>
1011
1112#include < hwreg/bitfields.h>
1213
1314namespace hwreg {
1415
16+ namespace internal {
17+
18+ inline zx::result<> MapError (fuchsia_hardware_spmi::DriverError error) {
19+ switch (error) {
20+ case fuchsia_hardware_spmi::DriverError::kInternal :
21+ return zx::error (ZX_ERR_INTERNAL);
22+ case fuchsia_hardware_spmi::DriverError::kNotSupported :
23+ return zx::error (ZX_ERR_NOT_SUPPORTED);
24+ case fuchsia_hardware_spmi::DriverError::kInvalidArgs :
25+ return zx::error (ZX_ERR_INVALID_ARGS);
26+ case fuchsia_hardware_spmi::DriverError::kBadState :
27+ return zx::error (ZX_ERR_BAD_STATE);
28+ case fuchsia_hardware_spmi::DriverError::kIoRefused :
29+ return zx::error (ZX_ERR_IO_REFUSED);
30+ default :
31+ return zx::error (ZX_ERR_NOT_FOUND);
32+ }
33+ }
34+
35+ // Methods to convert between host and big/little endian for the SPMI bus.
36+ static inline uint32_t HostToBigEndian (uint32_t val) { return htobe32 (val); }
37+ static inline uint16_t HostToBigEndian (uint16_t val) { return htobe16 (val); }
38+ static inline uint8_t HostToBigEndian (uint8_t val) { return val; }
39+ static inline uint32_t BigEndianToHost (uint32_t val) { return betoh32 (val); }
40+ static inline uint16_t BigEndianToHost (uint16_t val) { return betoh16 (val); }
41+ static inline uint8_t BigEndianToHost (uint8_t val) { return val; }
42+ static inline uint32_t HostToLittleEndian (uint32_t val) { return htole32 (val); }
43+ static inline uint16_t HostToLittleEndian (uint16_t val) { return htole16 (val); }
44+ static inline uint8_t HostToLittleEndian (uint8_t val) { return val; }
45+ static inline uint32_t LittleEndianToHost (uint32_t val) { return letoh32 (val); }
46+ static inline uint16_t LittleEndianToHost (uint16_t val) { return letoh16 (val); }
47+ static inline uint8_t LittleEndianToHost (uint8_t val) { return val; }
48+
49+ } // namespace internal
50+
51+ struct LittleEndian ;
52+ struct BigEndian ;
53+
54+ // Convert host to spmi byte order.
55+ template <typename T, class ByteOrder >
56+ static T ConvertToSpmiByteOrder (T value) {
57+ if (std::is_same<ByteOrder, BigEndian>::value) {
58+ // Big Endian
59+ return internal::HostToBigEndian (value);
60+ } else {
61+ // Little Endian / default (for 1 byte)
62+ return internal::HostToLittleEndian (value);
63+ }
64+ }
65+
66+ // Convert from spmi byte order to host.
67+ template <typename T, class ByteOrder >
68+ static T ConvertFromSpmiByteOrder (T value) {
69+ if (std::is_same<ByteOrder, BigEndian>::value) {
70+ // Big Endian
71+ return internal::BigEndianToHost (value);
72+ } else {
73+ // Little Endian / default (for 1 byte)
74+ return internal::LittleEndianToHost (value);
75+ }
76+ }
77+
1578// An instance of SpmiRegisterBase represents a staging copy of a register,
1679// which can be written to the device's register using SPMI protocol. It knows the register's
1780// address and stores a value for the register. The actual write/read is done upon calling
@@ -20,9 +83,18 @@ namespace hwreg {
2083// All usage rules of RegisterBase applies here with the following exceptions
2184// - ReadFrom()/WriteTo() methods will return zx_status_t instead of the class object and hence
2285// cannot be used in method chaining.
23- template <class DerivedType , class IntType , class PrinterState = void >
86+ template <class DerivedType , class IntType , class ByteOrder = void , class PrinterState = void >
2487class SpmiRegisterBase : public RegisterBase <DerivedType, IntType, PrinterState> {
88+ static_assert (std::is_same<ByteOrder, void >::value ||
89+ std::is_same<ByteOrder, LittleEndian>::value ||
90+ std::is_same<ByteOrder, BigEndian>::value,
91+ " unsupported byte order" );
92+ // Byte order must be specified if register address/value is more than one byte
93+ static_assert (!((sizeof (IntType) > 1 ) && std::is_same<ByteOrder, void >::value),
94+ " Byte order must be specified" );
95+
2596 public:
97+ using SpmiByteOrder = ByteOrder;
2698 // Delete base class ReadFrom() and WriteTo() methods and define new ones
2799 // which return zx_status_t in case of SPMI read/write failure
28100 template <typename T>
@@ -40,7 +112,7 @@ class SpmiRegisterBase : public RegisterBase<DerivedType, IntType, PrinterState>
40112 return zx::error (response.status ());
41113 }
42114 if (response.value ().is_error ()) {
43- return MapError (response.value ().error_value ()).take_error ();
115+ return internal:: MapError (response.value ().error_value ()).take_error ();
44116 }
45117
46118 if (response.value ().value ()->data .count () != sizeof (IntType)) {
@@ -49,42 +121,28 @@ class SpmiRegisterBase : public RegisterBase<DerivedType, IntType, PrinterState>
49121
50122 IntType value;
51123 memcpy (&value, response.value ().value ()->data .data (), sizeof (value));
124+ value = ConvertFromSpmiByteOrder<IntType, SpmiByteOrder>(value);
52125 return zx::ok (RegisterBaseType::set_reg_value (value));
53126 }
54127
55128 zx::result<> WriteTo (const fidl::ClientEnd<fuchsia_hardware_spmi::Device>& client) {
56129 uint32_t addr = RegisterBaseType::reg_addr ();
57130 IntType value = RegisterBaseType::reg_value ();
58131
59- std::vector<IntType> vector{value};
132+ value = ConvertToSpmiByteOrder<IntType, SpmiByteOrder>(value);
133+
134+ std::vector<uint8_t > vector{static_cast <uint8_t *>(&value),
135+ static_cast <uint8_t *>(&value) + sizeof (IntType)};
60136 auto response = fidl::WireCall (client)->ExtendedRegisterWriteLong (
61- addr, fidl::VectorView<IntType >::FromExternal (vector));
137+ addr, fidl::VectorView<uint8_t >::FromExternal (vector));
62138 if (!response.ok ()) {
63139 return zx::error (response.status ());
64140 }
65141 if (response.value ().is_error ()) {
66- return MapError (response.value ().error_value ()).take_error ();
142+ return internal:: MapError (response.value ().error_value ()).take_error ();
67143 }
68144 return zx::ok ();
69145 }
70-
71- private:
72- zx::result<> MapError (fuchsia_hardware_spmi::DriverError error) {
73- switch (error) {
74- case fuchsia_hardware_spmi::DriverError::kInternal :
75- return zx::error (ZX_ERR_INTERNAL);
76- case fuchsia_hardware_spmi::DriverError::kNotSupported :
77- return zx::error (ZX_ERR_NOT_SUPPORTED);
78- case fuchsia_hardware_spmi::DriverError::kInvalidArgs :
79- return zx::error (ZX_ERR_INVALID_ARGS);
80- case fuchsia_hardware_spmi::DriverError::kBadState :
81- return zx::error (ZX_ERR_BAD_STATE);
82- case fuchsia_hardware_spmi::DriverError::kIoRefused :
83- return zx::error (ZX_ERR_IO_REFUSED);
84- default :
85- return zx::error (ZX_ERR_NOT_FOUND);
86- }
87- }
88146};
89147
90148// An instance of SpmiRegisterAddr represents a typed register address: It
@@ -99,8 +157,11 @@ template <class RegType>
99157class SpmiRegisterAddr : public RegisterAddr <RegType> {
100158 public:
101159 static_assert (
102- std::is_base_of<SpmiRegisterBase<RegType, typename RegType::ValueType>, RegType>::value ||
103- std::is_base_of<SpmiRegisterBase<RegType, typename RegType::ValueType, EnablePrinter>,
160+ std::is_base_of<
161+ SpmiRegisterBase<RegType, typename RegType::ValueType, typename RegType::SpmiByteOrder>,
162+ RegType>::value ||
163+ std::is_base_of<SpmiRegisterBase<RegType, typename RegType::ValueType,
164+ typename RegType::SpmiByteOrder, EnablePrinter>,
104165 RegType>::value,
105166 " Parameter of SpmiRegisterAddr<> should derive from SpmiRegisterAddr" );
106167
@@ -109,8 +170,87 @@ class SpmiRegisterAddr : public RegisterAddr<RegType> {
109170 template <typename T>
110171 RegType ReadFrom (T* reg_io) = delete;
111172
173+ zx::result<RegType> ReadFrom (const fidl::ClientEnd<fuchsia_hardware_spmi::Device>& client) {
174+ RegType reg;
175+ reg.set_reg_addr (RegisterAddr<RegType>::addr ());
176+ return reg.ReadFrom (client);
177+ }
178+
112179 SpmiRegisterAddr (uint32_t reg_addr) : RegisterAddr<RegType>(reg_addr) {}
113180};
181+
182+ // Helper for consolidating contiguous reads/writes to SPMI registers.
183+ class SpmiRegisterArray {
184+ public:
185+ SpmiRegisterArray (uint32_t base_address, size_t size) : base_address_(base_address) {
186+ regs_.resize (size);
187+ }
188+
189+ zx::result<SpmiRegisterArray> ReadFrom (
190+ const fidl::ClientEnd<fuchsia_hardware_spmi::Device>& client) {
191+ uint32_t address = base_address_;
192+ size_t size = regs_.size ();
193+ auto data = regs_.data ();
194+
195+ while (size) {
196+ size_t read_size =
197+ std::min<size_t >(size, fuchsia_hardware_spmi::wire::kMaxExtendedLongTransferSize );
198+ auto response = fidl::WireCall (client)->ExtendedRegisterReadLong (address, read_size);
199+ if (!response.ok ()) {
200+ return zx::error (response.status ());
201+ }
202+ if (response.value ().is_error ()) {
203+ return internal::MapError (response.value ().error_value ()).take_error ();
204+ }
205+
206+ if (response.value ().value ()->data .count () != read_size) {
207+ return zx::error (ZX_ERR_BAD_STATE);
208+ }
209+
210+ memcpy (data, response.value ().value ()->data .data (), read_size);
211+
212+ size -= read_size;
213+ address += read_size;
214+ data += read_size;
215+ }
216+ return zx::ok (*this );
217+ }
218+
219+ zx::result<> WriteTo (const fidl::ClientEnd<fuchsia_hardware_spmi::Device>& client) {
220+ uint32_t address = base_address_;
221+ size_t size = regs_.size ();
222+ auto data = regs_.data ();
223+
224+ while (size) {
225+ size_t write_size =
226+ std::min<size_t >(size, fuchsia_hardware_spmi::wire::kMaxExtendedLongTransferSize );
227+ auto response = fidl::WireCall (client)->ExtendedRegisterWriteLong (
228+ address, fidl::VectorView<uint8_t >::FromExternal (data, write_size));
229+ if (!response.ok ()) {
230+ return zx::error (response.status ());
231+ }
232+ if (response.value ().is_error ()) {
233+ return internal::MapError (response.value ().error_value ()).take_error ();
234+ }
235+
236+ size -= write_size;
237+ address += write_size;
238+ data += write_size;
239+ }
240+ return zx::ok ();
241+ }
242+
243+ // `regs()` accesses register values of contiguous addresses from `base_address_` in
244+ // SpmiByteOrder. Callers of this function should use `CovertTo/FromSpmiByteOrder` to get the
245+ // correct endianness for host. Callers should also pay attention to alignment issues. If needed,
246+ // memcpy to/from a new buffer.
247+ std::vector<uint8_t >& regs () { return regs_; }
248+
249+ private:
250+ uint32_t base_address_;
251+ std::vector<uint8_t > regs_;
252+ };
253+
114254} // namespace hwreg
115255
116256#endif // SRC_DEVICES_SPMI_LIB_HWREG_SPMI_INCLUDE_HWREG_SPMI_H_
0 commit comments