.. _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, in 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 use static addresses as dynamic address 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