zephyr/doc/hardware/peripherals/i3c.rst

364 lines
12 KiB
ReStructuredText

.. _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_GROUP_ADDR`
* :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