.. _i3c_api:

Improved Inter-Integrated Circuit (I3C) Bus
###########################################

I3C (Improved Inter-Integrated Circuit) is a two-signal shared
peripheral interface bus.  Devices on the bus can operate in
two roles: as a "controller" that initiates transactions and
controls the clock, or as a "target" that responds to transaction
commands.

Currently, the API is based on `I3C Specification`_ version 1.1.1.

.. contents::
    :local:
    :depth: 2

.. _i3c-controller-api:

I3C Controller API
******************

Zephyr's I3C controller API is used when an I3C controller controls
the bus, particularly the start and stop conditions and the clock.
This is the most common mode, used to interact with I3C target
devices such as sensors.

Due to the nature of the I3C, there are devices on the bus where
they may not have addresses when powered on. Therefore, an additional
dynamic address assignment needs to be carried out by the I3C
controller. Because of this, the controller needs to maintain
separate structures to keep track of device status. This can be done
at build time, for example, by creating arrays of device descriptors
for both I3C and I\ :sup:`2`\ C devices:

.. code-block:: c

   static struct i3c_device_desc i3c_device_array[] = I3C_DEVICE_ARRAY_DT_INST(inst);
   static struct i3c_i2c_device_desc i2c_device_array[] = I3C_I2C_DEVICE_ARRAY_DT_INST(inst);

The macros :c:macro:`I3C_DEVICE_ARRAY_DT_INST` and
:c:macro:`I3C_I2C_DEVICE_ARRAY_DT_INST` are helper macros to aid in
create arrays of device descriptors corresponding to the devicetree
nodes under the I3C controller.

Here is a list of generic steps for initializing the I3C
controller and the I3C bus inside the device driver
initialization function:

#. Initialize the data structure of the I3C controller device
   driver instance. The usual device defining macros such as
   :c:macro:`DEVICE_DT_INST_DEFINE` can be used, and the initialization
   function provided as a parameter to the macro.

   * The :c:struct:`i3c_addr_slots` and :c:struct:`i3c_dev_list` are
     structures to aid in address assignments and device list management.
     If this is being used, this struct needs to be initialized by calling
     :c:func:`i3c_addr_slots_init`. These two structures can also be used
     with various helper functions.

   * Initialize the device descriptors if needed by the controller
     driver.

#. Initialize the hardware, including but not limited to:

   * Setup pin mux and directions.

   * Setup the clock for the controller.

   * Power on the hardware.

   * Configure the hardware (e.g. SCL clock frequency).

#. Perform bus initialization. There is a generic helper function,
   :c:func:`i3c_bus_init`, which performs the following steps.
   This function can be used if the controller does not require
   any special handling during bus initialization.

   #. Do ``RSTDAA`` to reset dynamic addresses of connected devices.
      If any connected devices have already been assigned an address,
      the bookkeeping data structures do not have records of these,
      for example, at power-on. So it is a good idea to reset and
      assign them new addresses.

   #. Do ``DISEC`` to disable any events from devices.

   #. Do ``SETDASA`` to assign a dynamic address using the static address of the device
      if so desired.

      * ``SETAASA`` may not be supported for all connected devices
        to assign static addresses as dynamic addresses.

      * BCR and DCR need to be obtained separately to populate
        the relevant fields in the I3C target device descriptor
        struct.

   #. Do ``ENTDAA`` to start dynamic address assignment, if there are
      still devices without addresses.

      * If there is a device waiting for address, it will send
        its Provisioned ID, BCR, and DCR back. Match the received
        Provisioned ID to the list of registered I3C devices.

        * If there is a match, assign an address (either from
          the stated static address if ``SETDASA`` has not been
          done, or use a free address).

          * Also, set the BCR and DCR fields in the device descriptor
            struct.

        * If there is no match, depending on policy, it can be
          assigned a free address, or the device driver can stop
          the assignment process and errors out.

          * Note that the I3C API requires device descriptor to
            function. A device without a device descriptor cannot be
            accessed through the API.

      * This step can be skipped if there is no connected devices
        requiring DAA.

   #. These are optional but highly recommended:

      * Do ``GETMRL`` and ``GETMWL`` to get maximum read/write
        length.

      * Do ``GETMXDS`` to get maximum read/write speed and maximum
        read turnaround time.

      * The helper function, :c:func:`i3c_bus_init`, would retrieve
        basic device information such as BCR, DCR, MRL and MWL.

   #. Do ``ENEC`` to re-enable events from devices.

      * The helper function, :c:func:`i3c_bus_init`, only re-enables
        hot-join events. IBI event should only be enabled when
        enabling IBI of a device.

In-Band Interrupt (IBI)
=======================

If a target device can generate In-Band Interrupt (IBI),
the controller needs to be made aware of it.

* :c:func:`i3c_ibi_enable` to enable IBI of a target device.

  * Some controller hardware have IBI slots which need to be
    programmed so that the controller can recognize incoming IBIs
    from a particular target device.

    * If the hardware has IBI slots, :c:func:`i3c_ibi_enable`
      needs to program those IBI slots.

    * Note that there are usually limited IBI slots on
      the controller so this operation may fail.

  * The implementation in driver should also send the ``ENEC`` command
    to enable interrupt of this target device.

* :c:func:`i3c_ibi_disable` to disable IBI of a target device.

  * If controller hardware makes use of IBI slots, this will remove
    description of the target device from the slots.

  * The implementation in driver should also send the ``DISEC`` command
    to disable interrupt of this target device.

Device Tree
===========

Here is an example for defining a I3C controller in device tree:

.. code-block:: devicetree

   i3c0: i3c@10000 {
           compatible = "vendor,i3c";

           #address-cells = < 0x3 >;
           #size-cells = < 0x0 >;

           reg = < 0x10000 0x1000 >;
           interrupts = < 0x1F 0x0 >;

           pinctrl-0 = < &pinmux-i3c >;
           pinctrl-names = "default";

           i2c-scl-hz = < 400000 >;

           i3c-scl-hz = < 12000000 >;

           status = "okay";

           i3c-dev0: i3c-dev0@420000ABCD12345678 {
                   compatible = "vendor,i3c-dev";

                   reg = < 0x42 0xABCD 0x12345678 >;

                   status = "okay";
           };

           i2c-dev0: i2c-dev0@380000000000000050 {
                   compatible = "vendor-i2c-dev";

                   reg = < 0x38 0x0 0x50 >;

                   status = "okay";
           };
   };

I3C Devices
-----------

For I3C devices, the ``reg`` property has 3 elements:

* The first one is the static address of the device.

  * Can be zero if static address is not used. Address will be
    assigned during DAA (Dynamic Address Assignment).

  * If non-zero and property ``assigned-address`` is not set,
    this will be the address of the device after SETDASA
    (Set Dynamic Address from Static Address) is issued.

* Second element is the upper 16-bit of the Provisioned ID (PID)
  which contains the manufacturer ID left-shifted by 1. This is
  the bits 33-47 (zero-based) of the 48-bit Provisioned ID.

* Third element contains the lower 32-bit of the Provisioned ID
  which is a combination of the part ID (left-shifted by 16,
  bits 16-31 of the PID) and the instance ID (left-shifted by 12,
  bits 12-15 of the PID).

Note that the unit-address (the part after ``@``) must match
the ``reg`` property fully where each element is treated as
32-bit integer, combining to form a 96-bit integer. This is
required for properly generating device tree macros.

I\ :sup:`2`\ C Devices
----------------------

For I\ :sup:`2`\ C devices where the device driver has support for
working under I3C bus, the device node can be described as
a child of the I3C controller. If the device driver is written to
only work with I\ :sup:`2`\ C controllers, define the node under
the I\ :sup:`2`\ C virtual controller as described below.
Otherwise, the ``reg`` property, similar to I3C devices,
has 3 elements:

* The first one is the static address of the device. This must be
  a valid address as I\ :sup:`2`\ C devices do not support
  dynamic address assignment.

* Second element is always zero.

  * This is used by various helper macros to determine whether
    the device tree entry corresponds to a I\ :sup:`2`\ C device.

* Third element is the LVR (Legacy Virtual Register):

  * bit[31:8] are unused.

  * bit[7:5] are the I\ :sup:`2`\ C device index:

    * Index ``0``

      * I3C device has a 50 ns spike filter where it is not
        affected by high frequency on SCL.

    * Index ``1``

      * I\ :sup:`2`\ C device does not have a 50 ns spike filter but
        can work with high frequency on SCL.

    * Index ``2``

      * I3C device does not have a 50 ns spike filter and
        cannot work with high frequency on SCL.

  * bit[4] is the I\ :sup:`2`\ C mode indicator:

    * ``0`` is FM+ mode.

    * ``1`` is FM mode.

Similar to I3C devices, the unit-address must match the ``reg``
property fully where each element is treated as 32-bit integer,
combining to form a 96-bit integer.

Device Drivers for I3C Devices
==============================

All of the transfer functions of I3C controller API require
the use of device descriptors, :c:struct:`i3c_device_desc`.
This struct contains runtime information about a I3C device,
such as, its dynamic address, BCR, DCR, MRL and MWL. Therefore,
the device driver of a I3C device should grab a pointer to
this device descriptor from the controller using
:c:func:`i3c_device_find`. This function takes an ID parameter
of type :c:struct:`i3c_device_id` for matching. The returned
pointer can then be used in subsequent API calls to
the controller.

I\ :sup:`2`\ C Devices under I3C Bus
====================================

Since I3C is backward compatible with I\ :sup:`2`\ C, the I3C controller
API can accommodate I2C API calls without modifications if the controller
device driver implements the I2C API. This has the advantage of using
existing I2C devices without any modifications to their device drivers.
However, since the I3C controller API works on device descriptors,
any calls to I2C API will need to look up the corresponding device
descriptor from the I2C device address. This adds a bit of processing
cost to any I2C API calls.

On the other hand, a device driver can be extended to utilize native
I2C device support via the I3C controller API. During device
initialization, :c:func:`i3c_i2c_device_find` needs to be called to
retrieve the pointer to the device descriptor. This pointer can be used
in subsequent API calls.

Note that, with either methods mentioned above, the devicetree node of
the I2C device must be declared according to I3C standard:

The I\ :sup:`2`\ C virtual controller device driver provides a way to
interface I\ :sup:`2`\ C devices on the I3C bus where the associated
device drivers can be used as-is without modifications. This requires
adding an intermediate node in the device tree:

.. code-block:: devicetree

   i3c0: i3c@10000 {
           <... I3C controller related properties ...>
           <... Nodes of I3C devices, if any ...>

           i2c-dev0: i2c-dev0@420000000000000050 {
                   compatible = "vendor-i2c-dev";

                   reg = < 0x42 0x0 0x50 >;

                   status = "okay";
           };
   };

Configuration Options
*********************

Related configuration options:

* :kconfig:option:`CONFIG_I3C`
* :kconfig:option:`CONFIG_I3C_USE_IBI`
* :kconfig:option:`CONFIG_I3C_IBI_MAX_PAYLOAD_SIZE`
* :kconfig:option:`CONFIG_I3C_CONTROLLER_INIT_PRIORITY`

API Reference
*************

.. doxygengroup:: i3c_interface
.. doxygengroup:: i3c_ccc
.. doxygengroup:: i3c_addresses
.. doxygengroup:: i3c_target_device

.. _I3C Specification: https://www.mipi.org/specifications/i3c-sensor-specification
