doc/hardware/peripherals/i3c.rst
.. _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.
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.
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";
};
};
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.
2\ C DevicesFor 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.
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
Index 1
2\ C device does not have a 50 ns spike filter but
can work with high frequency on SCL.Index 2
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.
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.
2\ C Devices under I3C BusSince 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:
CONFIG_I3CCONFIG_I3C_USE_IBICONFIG_I3C_IBI_MAX_PAYLOAD_SIZECONFIG_I3C_CONTROLLER_INIT_PRIORITYAPI Reference
.. doxygengroup:: i3c_interface .. doxygengroup:: i3c_ccc .. doxygengroup:: i3c_addresses .. doxygengroup:: i3c_target_device
.. _I3C Specification: https://www.mipi.org/specifications/i3c-sensor-specification