xref: /aosp_15_r20/external/pigweed/pw_i2c/public/pw_i2c/register_device.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2021 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include "pw_bytes/endian.h"
17 #include "pw_bytes/span.h"
18 #include "pw_chrono/system_clock.h"
19 #include "pw_i2c/address.h"
20 #include "pw_i2c/device.h"
21 #include "pw_i2c/initiator.h"
22 #include "pw_result/result.h"
23 #include "pw_status/status.h"
24 #include "pw_status/try.h"
25 
26 namespace pw {
27 namespace i2c {
28 
29 enum class RegisterAddressSize {
30   k1Byte = 1,
31   k2Bytes = 2,
32   k4Bytes = 4,
33 };
34 
35 /// The common interface for I2C register devices. Contains methods to help
36 /// read and write the device's registers.
37 ///
38 /// @warning This interface assumes that you know how to consult your device's
39 /// datasheet to determine correct address sizes, data sizes, endianness, etc.
40 class RegisterDevice : public Device {
41  public:
42   /// This constructor specifies the endianness of the register address and
43   /// data separately. If your register address and data have the same
44   /// endianness and you'd like to specify them both with a single argument,
45   /// see the other `pw::i2c::RegisterDevice` constructor.
46   ///
47   /// @param[in] initiator A `pw::i2c::Initiator` instance for the bus that the
48   /// device is on.
49   ///
50   /// @param[in] address The address of the I2C device.
51   ///
52   /// @param[in] register_address_order The endianness of the register address.
53   ///
54   /// @param[in] data_order The endianness of the data.
55   ///
56   /// @param[in] register_address_size The size of the register address.
RegisterDevice(Initiator & initiator,Address address,endian register_address_order,endian data_order,RegisterAddressSize register_address_size)57   constexpr RegisterDevice(Initiator& initiator,
58                            Address address,
59                            endian register_address_order,
60                            endian data_order,
61                            RegisterAddressSize register_address_size)
62       : Device(initiator, address),
63         register_address_order_(register_address_order),
64         data_order_(data_order),
65         register_address_size_(register_address_size) {}
66 
67   /// This constructor specifies the endianness of the register address and
68   /// data with a single argument. If your register address and data have
69   /// different endianness, use the other `pw::i2c::RegisterDevice`
70   /// constructor.
71   ///
72   /// @param[in] initiator A `pw::i2c::Initiator` instance for the bus that the
73   /// device is on.
74   ///
75   /// @param[in] address The address of the I2C device.
76   ///
77   /// @param[in] order The endianness of both the register address and register
78   /// data.
79   ///
80   /// @param[in] register_address_size The size of the register address.
RegisterDevice(Initiator & initiator,Address address,endian order,RegisterAddressSize register_address_size)81   constexpr RegisterDevice(Initiator& initiator,
82                            Address address,
83                            endian order,
84                            RegisterAddressSize register_address_size)
85       : Device(initiator, address),
86         register_address_order_(order),
87         data_order_(order),
88         register_address_size_(register_address_size) {}
89 
90   /// Writes data to multiple contiguous registers starting at a specific
91   /// register. This method is byte-addressable.
92   ///
93   /// `register_address` and `register_data` use the endianness that was
94   /// provided when this `pw::i2c::RegisterDevice` instance was constructed.
95   ///
96   /// @pre This method assumes that you've verified that your device supports
97   /// bulk writes and that `register_data` is a correct size for your device.
98   ///
99   /// @param[in] register_address The register address to begin writing at.
100   ///
101   /// @param[in] register_data The data to write. Endianness is taken into
102   /// account if the data is 2 or 4 bytes.
103   ///
104   /// @param[in] buffer A buffer for constructing the write data. The size of
105   /// this buffer must be at least as large as the size of `register_address`
106   /// plus the size of `register_data`.
107   ///
108   /// @param[in] timeout The maximum duration to block waiting for both
109   /// exclusive bus access and the completion of the I2C transaction.
110   ///
111   /// @returns @rst
112   ///
113   /// .. pw-status-codes::
114   ///
115   ///    OK: The bulk write was successful.
116   ///
117   ///    DEADLINE_EXCEEDED: Unable to acquire exclusive bus access and
118   ///    complete the transaction in time.
119   ///
120   ///    FAILED_PRECONDITION: The interface is not initialized or enabled.
121   ///
122   ///    INTERNAL: An issue occurred while building ``register_data``.
123   ///
124   ///    INVALID_ARGUMENT: ``register_address`` is larger than the 10-bit
125   ///    address space.
126   ///
127   ///    OUT_OF_RANGE: The size of ``buffer`` is less than the size
128   ///    of ``register_address`` plus the size of ``register_data``.
129   ///
130   ///    UNAVAILABLE: The device took too long to respond to the NACK.
131   ///
132   /// @endrst
133   Status WriteRegisters(uint32_t register_address,
134                         ConstByteSpan register_data,
135                         ByteSpan buffer,
136                         chrono::SystemClock::duration timeout);
137 
138   /// Variant of `pw::i2c::RegisterDevice::WriteRegisters()` that requires
139   /// `register_data` to be exactly 8 bits.
140   Status WriteRegisters8(uint32_t register_address,
141                          span<const uint8_t> register_data,
142                          ByteSpan buffer,
143                          chrono::SystemClock::duration timeout);
144 
145   /// Variant of `pw::i2c::RegisterDevice::WriteRegisters()` that requires
146   /// `register_data` to be exactly 16 bits.
147   Status WriteRegisters16(uint32_t register_address,
148                           span<const uint16_t> register_data,
149                           ByteSpan buffer,
150                           chrono::SystemClock::duration timeout);
151 
152   /// Variant of `pw::i2c::RegisterDevice::WriteRegisters()` that requires
153   /// `register_data` to be exactly 32 bits.
154   Status WriteRegisters32(uint32_t register_address,
155                           span<const uint32_t> register_data,
156                           ByteSpan buffer,
157                           chrono::SystemClock::duration timeout);
158 
159   /// Reads data from multiple contiguous registers starting from a specific
160   /// offset or register. This method is byte-addressable.
161   ///
162   /// `register_address` and `return_data` use the endianness that was
163   /// provided when this `pw::i2c::RegisterDevice` instance was constructed.
164   ///
165   /// @pre This method assumes that you've verified that your device supports
166   /// bulk reads and that `return_data` is a correct size for your device.
167   ///
168   /// @param[in] register_address The register address to begin reading at.
169   ///
170   /// @param[out] return_data The area to read the data into. The amount of
171   /// data that will be read is equal to the size of this span. Endianness is
172   /// taken into account if this span is 2 or 4 bytes.
173   ///
174   /// @param[in] timeout The maximum duration to block waiting for both
175   /// exclusive bus access and the completion of the I2C transaction.
176   ///
177   /// @returns @rst
178   ///
179   /// .. pw-status-codes::
180   ///
181   ///    OK: The bulk read was successful.
182   ///
183   ///    DEADLINE_EXCEEDED: Unable to acquire exclusive bus access and
184   ///    complete the transaction in time.
185   ///
186   ///    FAILED_PRECONDITION: The interface is not initialized or enabled.
187   ///
188   ///    INTERNAL: An issue occurred while building ``return_data``.
189   ///
190   ///    INVALID_ARGUMENT: ``register_address`` is larger than the 10-bit
191   ///    address space.
192   ///
193   ///    UNAVAILABLE: The device took too long to respond to the NACK.
194   ///
195   /// @endrst
196   Status ReadRegisters(uint32_t register_address,
197                        ByteSpan return_data,
198                        chrono::SystemClock::duration timeout);
199 
200   /// Variant of `pw::i2c::RegisterDevice::ReadRegisters()` that requires
201   /// `return_data` to be exactly 8 bits.
202   Status ReadRegisters8(uint32_t register_address,
203                         span<uint8_t> return_data,
204                         chrono::SystemClock::duration timeout);
205 
206   /// Variant of `pw::i2c::RegisterDevice::ReadRegisters()` that requires
207   /// `return_data` to be exactly 16 bits.
208   Status ReadRegisters16(uint32_t register_address,
209                          span<uint16_t> return_data,
210                          chrono::SystemClock::duration timeout);
211 
212   /// Variant of `pw::i2c::RegisterDevice::ReadRegisters()` that requires
213   /// `return_data` to be exactly 32 bits.
214   Status ReadRegisters32(uint32_t register_address,
215                          span<uint32_t> return_data,
216                          chrono::SystemClock::duration timeout);
217 
218   /// Sends a register address to write to and then writes to that address.
219   ///
220   /// `register_address` and `register_data` use the endianness that was
221   /// provided when this `pw::i2c::RegisterDevice` instance was constructed.
222   ///
223   /// @pre This method assumes that you've verified that `register_data` is a
224   /// correct size for your device.
225   ///
226   /// @param[in] register_address The register address to write to.
227   ///
228   /// @param[in] register_data The data that should be written at the address.
229   /// The maximum allowed size is 4 bytes.
230   ///
231   /// @param[in] timeout The maximum duration to block waiting for both
232   /// exclusive bus access and the completion of the I2C transaction.
233   ///
234   /// @returns @rst
235   ///
236   /// .. pw-status-codes::
237   ///
238   ///    OK: The write was successful.
239   ///
240   ///    DEADLINE_EXCEEDED: Unable to acquire exclusive bus access
241   ///    and complete the transaction in time.
242   ///
243   ///    FAILED_PRECONDITION: The interface is not initialized or enabled.
244   ///
245   ///    INTERNAL: An issue occurred while writing the data.
246   ///
247   ///    INVALID_ARGUMENT: ``register_address`` is larger than the 10-bit
248   ///    address space.
249   ///
250   ///    UNAVAILABLE: The device took too long to respond to the NACK.
251   ///
252   /// @endrst
253   Status WriteRegister(uint32_t register_address,
254                        std::byte register_data,
255                        chrono::SystemClock::duration timeout);
256 
257   /// Variant of `pw::i2c::RegisterDevice::WriteRegister()` that writes exactly
258   /// 8 bits.
259   Status WriteRegister8(uint32_t register_address,
260                         uint8_t register_data,
261                         chrono::SystemClock::duration timeout);
262 
263   /// Variant of `pw::i2c::RegisterDevice::WriteRegister()` that writes exactly
264   /// 16 bits.
265   Status WriteRegister16(uint32_t register_address,
266                          uint16_t register_data,
267                          chrono::SystemClock::duration timeout);
268 
269   /// Variant of `pw::i2c::RegisterDevice::WriteRegister()` that writes exactly
270   /// 32 bits.
271   Status WriteRegister32(uint32_t register_address,
272                          uint32_t register_data,
273                          chrono::SystemClock::duration timeout);
274 
275   /// Sends a register address to read from and then reads from that address.
276   ///
277   /// `register_address` and the return data use the endianness that was
278   /// provided when this `pw::i2c::RegisterDevice` instance was constructed.
279   ///
280   /// @pre This method assumes that you've verified that the return data size
281   /// is a correct size for your device.
282   ///
283   /// @param[in] register_address The register address to read.
284   ///
285   /// @param[in] timeout The maximum duration to block waiting for both
286   /// exclusive bus access and the completion of the I2C transaction.
287   ///
288   /// @returns @rst
289   ///
290   /// .. pw-status-codes::
291   ///
292   ///    OK: Returns the register data.
293   ///
294   ///    DEADLINE_EXCEEDED: Unable to acquire exclusive bus access and
295   ///    complete the transaction in time.
296   ///
297   ///    FAILED_PRECONDITION: The interface is not initialized or enabled.
298   ///
299   ///    INTERNAL: An issue occurred while building the return data.
300   ///
301   ///    INVALID_ARGUMENT: ``register_address`` is larger than the 10-bit
302   ///    address space.
303   ///
304   ///    UNAVAILABLE: The device took too long to respond to the NACK.
305   ///
306   /// @endrst
307   Result<std::byte> ReadRegister(uint32_t register_address,
308                                  chrono::SystemClock::duration timeout);
309 
310   /// Variant of `pw::i2c::RegisterDevice::ReadRegister()` that returns exactly
311   /// 8 bits.
312   Result<uint8_t> ReadRegister8(uint32_t register_address,
313                                 chrono::SystemClock::duration timeout);
314 
315   /// Variant of `pw::i2c::RegisterDevice::ReadRegister()` that returns exactly
316   /// 16 bits.
317   Result<uint16_t> ReadRegister16(uint32_t register_address,
318                                   chrono::SystemClock::duration timeout);
319 
320   /// Variant of `pw::i2c::RegisterDevice::ReadRegister()` that returns exactly
321   /// 32 bits.
322   Result<uint32_t> ReadRegister32(uint32_t register_address,
323                                   chrono::SystemClock::duration timeout);
324 
325  private:
326   // Helper write registers.
327   Status WriteRegisters(uint32_t register_address,
328                         ConstByteSpan register_data,
329                         const size_t register_data_size,
330                         ByteSpan buffer,
331                         chrono::SystemClock::duration timeout);
332 
333   const endian register_address_order_;
334   const endian data_order_;
335   const RegisterAddressSize register_address_size_;
336 };
337 
WriteRegisters(uint32_t register_address,ConstByteSpan register_data,ByteSpan buffer,chrono::SystemClock::duration timeout)338 inline Status RegisterDevice::WriteRegisters(
339     uint32_t register_address,
340     ConstByteSpan register_data,
341     ByteSpan buffer,
342     chrono::SystemClock::duration timeout) {
343   return WriteRegisters(register_address,
344                         register_data,
345                         sizeof(decltype(register_data)::value_type),
346                         buffer,
347                         timeout);
348 }
349 
WriteRegisters8(uint32_t register_address,span<const uint8_t> register_data,ByteSpan buffer,chrono::SystemClock::duration timeout)350 inline Status RegisterDevice::WriteRegisters8(
351     uint32_t register_address,
352     span<const uint8_t> register_data,
353     ByteSpan buffer,
354     chrono::SystemClock::duration timeout) {
355   return WriteRegisters(register_address,
356                         as_bytes(register_data),
357                         sizeof(decltype(register_data)::value_type),
358                         buffer,
359                         timeout);
360 }
361 
WriteRegisters16(uint32_t register_address,span<const uint16_t> register_data,ByteSpan buffer,chrono::SystemClock::duration timeout)362 inline Status RegisterDevice::WriteRegisters16(
363     uint32_t register_address,
364     span<const uint16_t> register_data,
365     ByteSpan buffer,
366     chrono::SystemClock::duration timeout) {
367   return WriteRegisters(register_address,
368                         as_bytes(register_data),
369                         sizeof(decltype(register_data)::value_type),
370                         buffer,
371                         timeout);
372 }
373 
WriteRegisters32(uint32_t register_address,span<const uint32_t> register_data,ByteSpan buffer,chrono::SystemClock::duration timeout)374 inline Status RegisterDevice::WriteRegisters32(
375     uint32_t register_address,
376     span<const uint32_t> register_data,
377     ByteSpan buffer,
378     chrono::SystemClock::duration timeout) {
379   return WriteRegisters(register_address,
380                         as_bytes(register_data),
381                         sizeof(decltype(register_data)::value_type),
382                         buffer,
383                         timeout);
384 }
385 
WriteRegister(uint32_t register_address,std::byte register_data,chrono::SystemClock::duration timeout)386 inline Status RegisterDevice::WriteRegister(
387     uint32_t register_address,
388     std::byte register_data,
389     chrono::SystemClock::duration timeout) {
390   std::array<std::byte, sizeof(register_data) + sizeof(register_address)>
391       byte_buffer;
392   return WriteRegisters(register_address,
393                         span(&register_data, 1),
394                         sizeof(register_data),
395                         byte_buffer,
396                         timeout);
397 }
398 
WriteRegister8(uint32_t register_address,uint8_t register_data,chrono::SystemClock::duration timeout)399 inline Status RegisterDevice::WriteRegister8(
400     uint32_t register_address,
401     uint8_t register_data,
402     chrono::SystemClock::duration timeout) {
403   std::array<std::byte, sizeof(register_data) + sizeof(register_address)>
404       byte_buffer;
405   return WriteRegisters(register_address,
406                         as_bytes(span(&register_data, 1)),
407                         sizeof(register_data),
408                         byte_buffer,
409                         timeout);
410 }
411 
WriteRegister16(uint32_t register_address,uint16_t register_data,chrono::SystemClock::duration timeout)412 inline Status RegisterDevice::WriteRegister16(
413     uint32_t register_address,
414     uint16_t register_data,
415     chrono::SystemClock::duration timeout) {
416   std::array<std::byte, sizeof(register_data) + sizeof(register_address)>
417       byte_buffer;
418   return WriteRegisters(register_address,
419                         as_bytes(span(&register_data, 1)),
420                         sizeof(register_data),
421                         byte_buffer,
422                         timeout);
423 }
424 
WriteRegister32(uint32_t register_address,uint32_t register_data,chrono::SystemClock::duration timeout)425 inline Status RegisterDevice::WriteRegister32(
426     uint32_t register_address,
427     uint32_t register_data,
428     chrono::SystemClock::duration timeout) {
429   std::array<std::byte, sizeof(register_data) + sizeof(register_address)>
430       byte_buffer;
431   return WriteRegisters(register_address,
432                         as_bytes(span(&register_data, 1)),
433                         sizeof(register_data),
434                         byte_buffer,
435                         timeout);
436 }
437 
ReadRegisters8(uint32_t register_address,span<uint8_t> return_data,chrono::SystemClock::duration timeout)438 inline Status RegisterDevice::ReadRegisters8(
439     uint32_t register_address,
440     span<uint8_t> return_data,
441     chrono::SystemClock::duration timeout) {
442   // For a single byte, there's no endian data, and so we can return the
443   // data as is.
444   return ReadRegisters(
445       register_address, as_writable_bytes(return_data), timeout);
446 }
447 
ReadRegisters16(uint32_t register_address,span<uint16_t> return_data,chrono::SystemClock::duration timeout)448 inline Status RegisterDevice::ReadRegisters16(
449     uint32_t register_address,
450     span<uint16_t> return_data,
451     chrono::SystemClock::duration timeout) {
452   PW_TRY(
453       ReadRegisters(register_address, as_writable_bytes(return_data), timeout));
454 
455   // Post process endian information.
456   for (uint16_t& register_value : return_data) {
457     register_value = bytes::ReadInOrder<uint16_t>(data_order_, &register_value);
458   }
459 
460   return pw::OkStatus();
461 }
462 
ReadRegisters32(uint32_t register_address,span<uint32_t> return_data,chrono::SystemClock::duration timeout)463 inline Status RegisterDevice::ReadRegisters32(
464     uint32_t register_address,
465     span<uint32_t> return_data,
466     chrono::SystemClock::duration timeout) {
467   PW_TRY(
468       ReadRegisters(register_address, as_writable_bytes(return_data), timeout));
469 
470   // TODO: b/185952662 - Extend endian in pw_byte to support this conversion
471   //                    as optimization.
472   // Post process endian information.
473   for (uint32_t& register_value : return_data) {
474     register_value = bytes::ReadInOrder<uint32_t>(data_order_, &register_value);
475   }
476 
477   return pw::OkStatus();
478 }
479 
ReadRegister(uint32_t register_address,chrono::SystemClock::duration timeout)480 inline Result<std::byte> RegisterDevice::ReadRegister(
481     uint32_t register_address, chrono::SystemClock::duration timeout) {
482   std::byte data = {};
483   PW_TRY(ReadRegisters(register_address, span(&data, 1), timeout));
484   return data;
485 }
486 
ReadRegister8(uint32_t register_address,chrono::SystemClock::duration timeout)487 inline Result<uint8_t> RegisterDevice::ReadRegister8(
488     uint32_t register_address, chrono::SystemClock::duration timeout) {
489   uint8_t data = 0;
490   PW_TRY(ReadRegisters8(register_address, span(&data, 1), timeout));
491   return data;
492 }
493 
ReadRegister16(uint32_t register_address,chrono::SystemClock::duration timeout)494 inline Result<uint16_t> RegisterDevice::ReadRegister16(
495     uint32_t register_address, chrono::SystemClock::duration timeout) {
496   std::array<uint16_t, 1> data = {};
497   PW_TRY(ReadRegisters16(register_address, data, timeout));
498   return data[0];
499 }
500 
ReadRegister32(uint32_t register_address,chrono::SystemClock::duration timeout)501 inline Result<uint32_t> RegisterDevice::ReadRegister32(
502     uint32_t register_address, chrono::SystemClock::duration timeout) {
503   std::array<uint32_t, 1> data = {};
504   PW_TRY(ReadRegisters32(register_address, data, timeout));
505   return data[0];
506 }
507 
508 }  // namespace i2c
509 }  // namespace pw
510 
511 // TODO (zengk): Register modification.
512