xref: /aosp_15_r20/external/pigweed/seed/0117.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _seed-0117:
2
3=============
40117: I3C
5=============
6.. seed::
7   :number: 117
8   :name: I3C
9   :status: Accepted
10   :proposal_date: 2023-10-30
11   :authors: Jack Chen
12   :cl: 178350
13   :facilitator: Alexei Frolov
14
15-------
16Summary
17-------
18A new peripheral protocol, I3C (pronounced eye-three-see) was introduced to
19electronic world and it has been widely accepted by SoC and sensor
20manufacturers. This seed is to propose a new front-end library pw_i3c, to help
21communicate with devices on I3C bus.
22
23----------
24Motivation
25----------
26Though widely used, I²C peripheral bus has several significant shortages, for
27example the low bus speed, extra physical line to carry interrupt from each
28device on the I²C bus, etc. To cope with higher requirements and to address
29shortages of I²C, MIPI proposed a new fast, low-power and two-wire peripheral
30protocol, I3C.
31
32I3C could be regarded as improved I²C. But in the meantime, it is a new protocol
33which is different to I²C in both hardware and software. Some important
34differences include:
35
361. I3C SDA uses open-drain mode when necessary for legacy I²C compatibility,
37   but switches to push-pull outputs whenever possible.
382. I3C SCL runs in only in pull-pull mode and can only be driven by I3C
39   initiator.
403. I3C devices supports dynamic address assignment (DAA) and has higher address
41   requirements.
424. I3C needs bus initialization and DAA before it is ready to use.
435. I3C has a standardized command set, called common command codes (CCC).
446. I3C supports In-Band interrupt and hot-join.
457. I3C device is identified by a 48-bit Provisioned ID (Manufacturer ID + Part
46   ID + Instance ID).
47
48In conclusion, it is worth providing an independent library pw_i3c to help
49initialize I3C initiator and communicate with I3C devices on the bus.
50
51--------------------------
52Proposal (Detailed design)
53--------------------------
54Since I3C is very similar to I²C, following proposal is to create a standalone
55library pw_i3c, which shares a similar structure as pw_i2c.
56
57device type
58-----------
59
60The ``DeviceType`` is used to help differentiate legacy I²C devices and I3C
61devices on one I3C bus.
62
63.. code-block:: c++
64
65   enum class DeviceType : bool {
66     kI2c,
67     kI3c,
68   };
69
70Address
71-------
72
73The ``Address`` is a helper class to represent addresses which are used by
74``pw_i3c`` APIs. In I3C protocol, addresses are differentiated by I²C (static)
75address and I3C (active or dynamic) address. The reason why device type is
76embedded into address is that transactions for I²C and I3C devices are
77different. So ``Initiator`` could tell the device type just by the provided
78``Address`` object.
79
80Apart from creating and checking ``Address`` at compile time, a helper
81constructor to create and check ``Address`` at runtime is created, with
82following reasons:
83
841. A new active/dynamic address could be assigned to I3C devices at run time.
852. New devices could hot-join the bus at run time.
86
87.. code-block:: c++
88
89   class Address {
90    public:
91     static constexpr uint8_t kHotJoinAddress = 0x02;
92     static constexpr uint8_t kBroadcastAddress = 0x7E;
93     static constexpr uint8_t kStaticMaxAddress = 0x7F;
94     // For I3C, the active (dynamic) address restriction is dynamic (depending
95     // on devices on the bus, details can be found in "MIPI I3C Basic
96     // Specification Version 1.1.1" chapter 5.1.2.2.5). But to simplify the
97     // design, the strictest rule is applied. And there will be 108 addresses
98     // free for dynamic address chosen.
99     static constexpr uint8_t kDynamicMinAddress = 0x08;
100     static constexpr uint8_t kDynamicMaxAddress = 0x7D;
101
102     // Helper constructor to ensure the address fits in the address space at
103     // compile time, skipping the construction time assert.
104     template <DeviceType kDeviceType, uint8_t kAddress>
105     static constexpr Address Create() {
106       static_assert(!IsOutOfRange(kDeviceType, kAddress));
107       static_assert((DeviceType::kI2c == kDeviceType) ||
108                     !SingleBitErrorDetection(kAddress));
109       return Address{kDeviceType, kAddress};
110     }
111
112     // Helper constructor to create the address at run time.
113     // Returns std::nullopt if provided type and address does not fit address
114     // rules.
115     static constexpr std::optional<Address> Create(
116         DeviceType device_type, uint8_t address) {
117       if (IsOutOfRange(device_type, address)) {
118         return std::nullopt;
119       }
120       if (DeviceType::kI3c == device_type && SingleBitErrorDetection(address)) {
121         return std::nullopt;
122       }
123       return Address{type, address};
124     }
125
126     // Return the type of address.
127     constexpr DeviceType GetType() const;
128
129     // Return the address.
130     constexpr uint8_t GetAddress() const;
131
132    private:
133     static constexpr uint8_t kAddressMask = 0x7F;
134     static constexpr int kTypeShift = 7;
135     static constexpr uint8_t kTypeMask = 0x01;
136
137     constexpr Address(DeviceType type, uint8_t address)
138         : packed_address_{Pack(type, address)} {}
139
140     uint8_t packed_address_;
141   };
142
143Ccc
144---
145
146Common Command Codes are categorized into broadcast(Command Codes from 0x00 to
1470x7F) and direct(Command Codes from 0x80 to 0xFE). The rational behind it is
148broadcast CCC can only be write and is executed in one transaction, but direct
149CCC can be both write and read, and is executed in two transactions. We can
150eliminate extra CCC type check in initiator CCC API.
151
152.. code-block:: c++
153
154   enum class CccBroadcast : uint8_t {
155     kEnc = 0x00,
156     kDisec = 0x01,
157     kEntdas0 = 0x02,
158     ...
159     kSetxtime = 0x28,
160     kSetaasa = 0x29,
161   }
162
163   enum class CccDirect : uint8_t {
164     kEnc = 0x80,
165     kDisec = 0x81,
166     kEntas0 = 0x82,
167     ...
168     kSetxtime = 0x98,
169     kGetxtime = 0x99,
170   };
171
172   inline constexpr uint8_t kCccDirectBit = 7;
173
174   enum class CccAction : bool {
175     kWrite,
176     kRead,
177   };
178
179Initiator
180---------
181
182.. inclusive-language: disable
183
184Similar as ``pw::i2c``, ``Initiator`` is the common, base driver interface for
185initiating thread-safe transactions with devices on an I3C bus. Other
186documentation may call this style of interface an “master”, “central”, or
187“controller”.
188
189.. inclusive-language: enable
190
191The main difference by comparison with I²C, is I3C initiator needs a bus
192initialization and dynamic address assignment (DAA) step, before it is fully
193functional. And after first bus initialization, I3C initiator should be able to
194do bus re-initialization anytime when the bus is free. However, different
195backend implementations may deal with this part differently. For example, Linux
196does not expose I3C bus to userspace, which means users cannot control bus
197initialization. NXP Mcuxpresso SDK exposes I3C as a pure library to users, so it
198is users' responsibility to initialize the bus and perform DAA to get a usable
199initiator. Zephyr provides more functions than Linux regarding I3C, which
200makes I3C usage in Zephyr looks more likely to NXP Mcuxpresso SDK.
201
202Considering the complexity of different backend implementations of I3C bus, it
203is better to have an "Initiator Maker" to take care of making an I3C
204``Initiator``. And this is not considered in the first version of ``pw_i3c``
205design.
206
207.. code-block:: c++
208
209   // PID is the unique identifier to an I3C device.
210   struct Pid {
211     uint16_t manuf_id = 0;
212     uint16_t part_id = 0;
213     uint16_t instance_id = 0;
214
215     friend constexpr bool operator==(Pid const& lhs, Pid const& rhs) {
216       return (lhs.manuf_id == rhs.manuf_id) && (lhs.part_id == rhs.part_id) &&
217              (lhs.instance_id == rhs.instance_id);
218     }
219     friend constexpr bool operator!=(Pid const& lhs, Pid const& rhs) {
220       return (lhs.manuf_id != rhs.manuf_id) || (lhs.part_id != rhs.part_id) ||
221              (lhs.instance_id != rhs.instance_id);
222     }
223
224    // Concat manuf_id, part_id and instance_id to a pid in 64-bit.
225    constexpr uint64_t AsUint64(Pid const pid) {
226      return (static_cast<uint64_t>(pid.manuf_id) << 33) |
227             (static_cast<uint64_t>(pid.part_id) << 16) |
228             (static_cast<uint64_t>(pid.instance_id) << 12);
229    }
230
231    // Split a 64-bit pid into manuf_id, part_id and instance_id (struct Pid).
232    static constexpr Pid FromUint64(const uint64_t pid) {
233      return Pid{
234          .manuf_id = ExtractBits<uint16_t, 47, 33>(pid),
235          .part_id = ExtractBits<uint16_t, 31, 16>(pid),
236          .instance_id = ExtractBits<uint16_t, 15, 12>(pid)};
237    }
238
239  };
240
241  // I3C supports in-band interrupt (IBI), but generalize the name to cover
242  // traditional interrupts.
243  // For IBI, the argument can be used to store data transferred.
244  // If the handler is queued in a workqueue, the data could be of any length.
245  // If the handler is executed inside CPU ISR directly, the data should only
246  // be mandatory data byte.
247  using InterruptHandler = ::pw::Function<void(ByteSpan)>;
248
249  class Initiator {
250   public:
251    virtual ~Initiator() = default;
252
253    // Pid is the unique identifier to an I3C device and it should be known to
254    // users through datasheets, like static addresses to I²C or I3C devices.
255    // But active (dynamic) address to an I3C device is changeable during
256    // run-time. Users could use this API to retrieve active address through
257    // PID.
258    //
259    // There are other information which users may be interested to know about
260    // an I3C device, like Bus Characteristics Register (BCR) and Device
261    // Characteristic Register(s) (DCR). But they can be read through direct
262    // read CCC API (ReadDirectCcc).
263    //
264    // Returns:
265    // Dynamic Address - Success.
266    // NOT_FOUND - Provided pid does not match with any active i3c device.
267    Result<Address> RetrieveDynamicAddressByPid(Pid pid);
268
269    // Perform a broadcast CCC transaction.
270    // ccc_id: the broadcast CCC ID.
271    // buffer: payload to broadcast.
272    //
273    // Returns:
274    // OK - Success.
275    // INVALID_ARGUMENT - provided ccc_id is not supported.
276    // UNAVAILABLE - NACK condition occurred, meaning there are no active I3C
277    //   devices on the bus.
278    // Other status codes as defined by backend.
279    Status WriteBroadcastCcc(CccBroadcast ccc_id, ConstByteSpan buffer);
280    Status WriteBroadcastCcc(CccBroadcast ccc_id,
281                             const void* buffer,
282                             size_t size_bytes);
283
284    // Perform a direct write CCC transaction.
285    // ccc_id: the direct CCC ID.
286    // device_address: the address which the CCC targets for.
287    // buffer: payload to write.
288    //
289    // Returns:
290    // OK - Success.
291    // INVALID_ARGUMENT - provided ccc_id is not supported, or device_address is
292    //   for I3C devices.
293    // UNAVAILABLE - NACK condition occurred, meaning there is no active I3C
294    //   device with the provided device_address or it is busy now.
295    // Other status codes as defined by backend.
296    Status WriteDirectCcc(CccDirect ccc_id,
297                          Address device_address,
298                          ConstByteSpan buffer);
299    Status WriteDirectCcc(CccDirect ccc_id,
300                          Address device_address,
301                          const void* buffer,
302                          size_t size_bytes);
303
304    // Perform a direct read CCC transaction.
305    // ccc_id: the direct CCC ID.
306    // device_address: the address which the CCC targets for.
307    // buffer: payload to read.
308    //
309    // Returns:
310    // OK - Success.
311    // INVALID_ARGUMENT - provided ccc_id is not supported, or device_address is
312    //   for I3C devices.
313    // UNAVAILABLE - NACK condition occurred, meaning there is no active I3C
314    //   device with the provided device_address or it is busy now.
315    // Other status codes as defined by backend.
316    Status ReadDirectCcc(CccDirect ccc_id,
317                         Address device_address,
318                         ByteSpan buffer);
319    Status ReadDirectCcc(CccDirect ccc_id,
320                         Address device_address,
321                         void* buffer,
322                         size_t size_bytes);
323
324    // Write bytes and read bytes (this is normally executed in two independent
325    // transactions).
326    //
327    // Timeout is no longer needed in I3C transactions because only I3C
328    // initiator drives the clock in push-pull mode, and devices on the bus
329    // cannot stretch the clock.
330    //
331    // Returns:
332    // Ok - Success.
333    // UNAVAILABLE - NACK condition occurred, meaning the addressed device did
334    //   not respond or was unable to process the request.
335    // Other status codes as defined by backend.
336    Status WriteReadFor(Address device_address,
337                        ConstByteSpan tx_buffer,
338                        ByteSpan rx_buffer);
339    Status WriteReadFor(I3cResponder device,
340                        const void* tx_buffer,
341                        size_t tx_size_bytes,
342                        void* rx_buffer,
343                        size_t rx_size_bytes);
344
345    // Write bytes.
346    //
347    // Returns:
348    // OK - Success.
349    // UNAVAILABLE - NACK condition occurred, meaning the addressed device did
350    //   not respond or was unable to process the request.
351    // Other status codes as defined by backend.
352    Status WriteFor(Address device_address, ConstByteSpan tx_buffer);
353    Status WriteFor(Address device_address,
354                    const void* tx_buffer,
355                    size_t tx_size_bytes);
356
357    // Read bytes.
358    //
359    // Returns:
360    // OK - Success.
361    // UNAVAILABLE - NACK condition occurred, meaning the addressed device did
362    //   not respond or was unable to process the request.
363    // Other status codes as defined by backend.
364    Status ReadFor(Address device_address, ByteSpan rx_buffer);
365    Status ReadFor(Address device_address,
366                   void* rx_buffer,
367                   size_t rx_size_bytes);
368
369    // Probes the device for an ACK after only writing the address.
370    // This is done by attempting to read a single byte from the specified
371    // device.
372    //
373    // Returns:
374    // OK - Success.
375    // UNAVAILABLE - NACK condition occurred, meaning the addressed device did
376    //   not respond or was unable to process the request.
377    Status ProbeDeviceFor(Address device_address);
378
379    // Sets a (IBI) handler to execute when an interrupt is triggered from a
380    // device with the provided address. Handler for one address should be
381    // registered only once, unless it is cleared. Registration twice with
382    // same address should fail.
383    //
384    // Note that hot-join handler could be registered with this function since
385    // hot-join is sent through IBI.
386    //
387    // This handler is finally executed by I3C initiator, which means it may
388    // include any valid I3C actions (write and read). When I3C write/read
389    // happens, the interrupt handler is more like an I3C transaction than a
390    // traditional interrupt. Different I3C initiators may execute the handler
391    // in different ways. Some may queue the work on a workqueue and some may
392    // execute the handler directly inside IBI IRQ. Users should be aware of the
393    // backend algorithm and when execution happens in IBI IRQ, they should just
394    // read the mandatory data byte out through the handler and perform other
395    // actions in a different thread.
396    //
397    // Warning:
398    // This method is not thread-safe and cannot be used in interrupt handlers.
399    //
400    // Returns:
401    // OK - The interrupt handler was configured.
402    // INVALID_ARGUMENT - The handler is empty, or the handler is for IBI but
403    //   the address is not for an I3C device.
404    // Other status codes as defined by the backend.
405    Status SetInterruptHandler(Address device_address,
406                               InterruptHandler&& handler);
407
408    // Clears the interrupt handler.
409    //
410    // Warning:
411    // This method is not thread-safe and cannot be used in interrupt handlers.
412    //
413    // Returns:
414    // OK - The interrupt handler was cleared
415    // INVALID_ARGUMENT - the handler is for IBI but the address is not for an
416    //    I3C device.
417    // Other status codes as defined by the backend.
418    Status ClearInterruptHandler(Address device_address);
419
420    // Enables interrupts which will trigger the interrupt handler.
421    //
422    // Warning:
423    // This method is not thread-safe and cannot be used in interrupt handlers.
424    //
425    // Preconditions:
426    // A handler has been set using `SetInterruptHandler()`.
427    //
428    // Returns:
429    // OK - The interrupt handler was enabled.
430    // INVALID_ARGUMENT - the handler is for IBI but the address is not for an
431    //    I3C device.
432    // Other status codes as defined by the backend.
433    Status EnableInterruptHandler(Address device_address);
434
435    // Disables interrupts which will trigger the interrupt handler.
436    //
437    // Warning:
438    // This method is not thread-safe and cannot be used in interrupt handlers.
439    //
440    // Preconditions:
441    // A handler has been set using `SetInterruptHandler()`.
442    //
443    // Returns:
444    // OK - The interrupt handler was disabled.
445    // INVALID_ARGUMENT - the handler is for IBI but the address is not for an
446    //    I3C device.
447    // Other status codes as defined by the backend.
448    Status DisableInterruptHandler(Address device_address);
449
450   private:
451    virtual Result<Address> DoRetrieveDynamicAddressByPid(Pid pid) = 0;
452    virtual Status DoTransferCcc(CccAction read_or_write,
453                                 uint8_t ccc_id,
454                                 std::optional<Address> device_address,
455                                 ByteSpan buffer) = 0;
456    virtual Status DoWriteReadFor(Address device_address,
457                                  ConstByteSpan tx_buffer,
458                                  ByteSpan rx_buffer) = 0;
459    virtual Status DoSetInterruptHandler(Address address,
460                                         InterruptHandler&& handler) = 0;
461    virtual Status DoEnableInterruptHandler(Address address, bool enable) = 0;
462  };
463
464Device
465------
466
467Same as ``pw::i2c::Device``, a ``Device`` class is used in ``pw::i3c`` to
468write/read arbitrary chunks of data over a bus to a specific device, or perform
469other I3C operations, e.g. direct CCC.
470Though PID is the unique identifier for I3C devices, considering backward
471compatibility with I²C devices, this object also wraps the Initiator API with
472an active ``Address``. Application should initiate or be notified the
473``Address`` change and update the ``Address`` in ``Device`` object.
474
475.. code-block:: c++
476
477   class Device {
478    public:
479     // It is users' responsibility to get and pass the active (dynamic) address
480     // when creating a ``Device``. If the dynamic address of an I3C device is
481     // unknown, users could get it through initiator:
482     // pw::i3c::Initiator::RetrieveDynamicAddressByPid();
483     constexpr Device(Initiator& initiator, Address device_address, Pid pid)
484        : initiator_(initiator),
485          device_address_(device_address),
486          pid_(pid) {}
487
488     // For I2C devices connected to I3C bus, pid_ is default-initialized to be
489     // std::nullopt.
490     constexpr Device(Initiator& initiator, Address device_address)
491        : initiator_(initiator),
492          device_address_(device_address) {}
493
494     Device(const Device&) = delete;
495     Device(Device&&) = default;
496     ~Device() = default;
497
498     // Perform a direct write CCC transaction.
499     // ccc_id: the direct CCC ID.
500     // buffer: payload to write.
501     //
502     // Returns:
503     // OK - Success.
504     // INVALID_ARGUMENT - provided ccc_id is not supported, or device_address_
505     //   is not for I3C devices.
506     // UNAVAILABLE - NACK condition occurred, meaning there is no active I3C
507     //   device with the provided device_address or it is busy now.
508     // Other status codes as defined by backend.
509     Status WriteDirectCcc(CccDirect ccc_id, ConstByteSpan buffer);
510     Status WriteDirectCcc(CccDirect ccc_id,
511                           const void* buffer,
512                           size_t size_bytes);
513
514     // Perform a direct read CCC transaction.
515     // ccc_id: the direct CCC ID.
516     // buffer: payload to read.
517     //
518     // Returns:
519     // OK - Success.
520     // INVALID_ARGUMENT - provided ccc_id is not supported, or device_address_
521     //   is not for I3C devices.
522     // UNAVAILABLE - NACK condition occurred, meaning there is no active I3C
523     //   device with the provided device_address or it is busy now.
524     // Other status codes as defined by backend.
525     Status ReadDirectCcc(CccDirect ccc_id, ByteSpan buffer);
526     Status ReadDirectCcc(CccDirect ccc_id,
527                          const void* buffer,
528                          size_t size_bytes);
529
530     // Write bytes and read bytes (this is normally executed in two independent
531     // transactions).
532     //
533     // Timeout is no longer needed in I3C transactions because only I3C
534     // initiator drives the clock in push-pull mode, and devices on the bus
535     // cannot stretch the clock.
536     //
537     // Returns:
538     // OK - Success.
539     // UNAVAILABLE - NACK condition occurred, meaning the addressed device did
540     //   not respond or was unable to process the request.
541     // Other status codes as defined by backend.
542     Status WriteReadFor(ConstByteSpan tx_buffer, ByteSpan rx_buffer);
543     Status WriteReadFor(const void* tx_buffer,
544                         size_t tx_size_bytes,
545                         void* rx_buffer,
546                         size_t rx_size_bytes);
547
548     // Write bytes.
549     //
550     // Returns:
551     // OK - Success.
552     // UNAVAILABLE - NACK condition occurred, meaning the addressed device did
553     //   not respond or was unable to process the request.
554     // Other status codes as defined by backend.
555     Status WriteFor(ConstByteSpan tx_buffer);
556     Status WriteFor(const void* tx_buffer, size_t tx_size_bytes);
557
558     // Read bytes.
559     //
560     // Returns:
561     // Ok - Success.
562     // UNAVAILABLE - NACK condition occurred, meaning the addressed device did
563     //   not respond or was unable to process the request.
564     // Other status codes as defined by backend.
565     Status ReadFor(ByteSpan rx_buffer);
566     Status ReadFor(void* rx_buffer, size_t rx_size_bytes);
567
568     // Probes the device for an ACK after only writing the address.
569     // This is done by attempting to read a single byte from this device.
570     //
571     // Returns:
572     // Ok - Success.
573     // UNAVAILABLE - NACK condition occurred, meaning the addressed device did
574     //   not respond or was unable to process the request.
575     Status ProbeFor();
576
577     // Sets a (IBI) handler to execute when an interrupt is triggered from a
578     // device with the provided address. Handler for one address should be
579     // registered only once, unless it is cleared. Registration twice with
580     // same address should fail.
581     //
582     // This handler is finally executed by I3C initiator, which means it may
583     // include any valid I3C actions (write and read). When I3C write/read
584     // happens, the interrupt handler is more like an I3C transaction than a
585     // traditional interrupt. Different I3C initiators may execute the handler
586     // in different ways. Some may queue the work on a workqueue and some may
587     // execute the handler directly inside IBI IRQ. Users should be aware of the
588     // backend algorithm and when execution happens in IBI IRQ, they should just
589     // read the mandatory data byte out through the handler and perform other
590     // actions in a different thread.
591     //
592     // Warning:
593     // This method is not thread-safe and cannot be used in interrupt handlers.
594     // Do not register hot-join handler for I3C devices as hot-join handler is
595     // initiator specific, not for a single device.
596     //
597     // Returns:
598     // OK - The interrupt handler was configured.
599     // INVALID_ARGUMENT - The handler is empty, or the handler is for IBI but
600     //   the device is an I3C device.
601     // Other status codes as defined by the backend.
602     Status SetInterruptHandler(InterruptHandler&& handler);
603
604     // Clears the interrupt handler.
605     //
606     // Warning:
607     // This method is not thread-safe and cannot be used in interrupt handlers.
608     //
609     // Returns:
610     // OK - The interrupt handler was cleared
611     // INVALID_ARGUMENT - the handler is for IBI but the device is an I3C device.
612     // Other status codes as defined by the backend.
613     Status ClearInterruptHandler();
614
615     // Enables interrupts which will trigger the interrupt handler.
616     //
617     // Warning:
618     // This method is not thread-safe and cannot be used in interrupt handlers.
619     //
620     // Preconditions:
621     // A handler has been set using `SetInterruptHandler()`.
622     //
623     // Returns:
624     // OK - The interrupt handler was enabled.
625     // INVALID_ARGUMENT - the handler is for IBI but the device is an I3C device.
626     // Other status codes as defined by the backend.
627     Status EnableInterruptHandler();
628
629     // Disables interrupts which will trigger the interrupt handler.
630     //
631     // Warning:
632     // This method is not thread-safe and cannot be used in interrupt handlers.
633     //
634     // Preconditions:
635     // A handler has been set using `SetInterruptHandler()`.
636     //
637     // Returns:
638     // OK - The interrupt handler was disabled.
639     // INVALID_ARGUMENT - the handler is for IBI but the device is an I3C device.
640     // Other status codes as defined by the backend.
641     Status DisableInterruptHandler();
642
643     // Update device address during run-time actively.
644     //
645     // Warning:
646     // This function is dedicatedly to I3C devices.
647     void UpdateAddressActively(Address new_address);
648
649     // Update device address during run-time Passively.
650     // initiator_ will be responsible for retrieving the dynamic address and
651     // substitute the device_address_.
652     //
653     // Warning:
654     // This function is dedicatedly to I3C devices.
655     //
656     // Returns:
657     // OK - Success.
658     // UNIMPLEMENTED - pid is empty (I²C device).
659     // NOT_FOUND - initiator_ fails to retrieve dynamic address based on pid_.
660     Status UpdateAddressPassively();
661
662    private:
663     Initiator& initiator_;
664     Address device_address_;
665     std::optional<Pid> pid_;
666   };
667
668------------
669Alternatives
670------------
671Since I3C is similar to I²C and pw_i3c is similar to pw_i2c, instead of creating
672a standalone library, an alternative solution is to combine pw_i3c and pw_i2c,
673and providing a single library pw_ixc, or other suitable names. And in this
674comprehensive library, I3C-related features could be designed to be optional.
675
676On one hand, this solution could reuse a lot of code and simplify some work in
677user level, if users want to abstract usage of I²C and I3C in application.
678On the other hand, it also brings churn to existing projects using pw_i2c, and
679ambiguity and confusion in the long run (I²C is mature, but I3C is new and
680actively improving).
681
682--------------
683Open Questions
684--------------
6851. As mentioned in the description of I3C ``Initiator`` class, the creation of a
686   fully functional ``Initiator`` would be handled by a different class.
687
6882. Because there are two types of ``Address`` in I3C, the static and dynamic,
689   the ``pw::i3c::Address`` is not compatible with ``pw::i2c::Address``. So when
690   users try to create an ``Address`` for an I²C device, they need to carefully
691   choose the correct class depending on which bus the device is connected to.
692   This class may cause bigger concern if ``Address`` is needed to be shared
693   through interface in application code.
694   But the problem is resolvable by templating ``Address`` in caller code.
695   Also, we can have a helper function in ``pw::i3c::Address``, which consumes a
696   ``pw::i2c::Addres``s and create a ``pw::i3c::Address``. This helper will be
697   added and discussed further in a following patch.
698
6993. ``DeviceType`` is embedded into ``Address``. So ``pw::i3c::Initiator`` could
700   tell the device type (I²C or I3C) based on provided ``Address``. But this is
701   not necessary because Initiator has performed DAA during initialization so it
702   should know which addresses have been assigned to I3C devices.
703   In this case, the only advantage of this design is to help applying address
704   restriction during creating ``Address`` object. Should address restriction be
705   taken care of by HAL? Though fewer, I²C has reserved addresses, too, but they
706   are not checked in ``pw::i2c::Address``.
707
7084. ``RegisterDevice`` for I3C is the same as I²C, in protocol. To read/write
709   from a register, the ``Initiator`` sends an active address on the bus. Once
710   acknowledged, it will send the register address followed by data.
711   So the ideal design is to abstract ``RegisterDevice`` across I²C and I3C, or
712   maybe even other peripheral buses (e.g. SPI and DSI). However, the underlying
713   register operation functions are different. It is better to be handled in a
714   separate SEED.
715