Skip to content

Commit 12c39b4

Browse files
Ruby ZhuangCQ Bot
authored andcommitted
[hwreg][spmi] Support for SpmiRegisterArray
Also fix `WriteTo` type and endianness. Bug: b/374113073 Change-Id: Ie7f1285d916be3d1063c34383565270718171d8c Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/1149572 Commit-Queue: Ruby Zhuang <[email protected]> Reviewed-by: Braden Kell <[email protected]> Reviewed-by: Andres Oportus <[email protected]>
1 parent 721029d commit 12c39b4

File tree

1 file changed

+165
-25
lines changed
  • src/devices/spmi/lib/hwreg-spmi/include/hwreg

1 file changed

+165
-25
lines changed

src/devices/spmi/lib/hwreg-spmi/include/hwreg/spmi.h

Lines changed: 165 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,76 @@
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

1314
namespace 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>
2487
class 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>
99157
class 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

Comments
 (0)