hardware/peripherals/i3c.html · hardware/peripherals/i3c.rst 공식 원문 ↗

Zephyr 3.7.0 · Hardware · Peripherals · I3C

Improved Inter-Integrated Circuit(I3C) Bus

I3C bus가 address 없는 target을 발견·할당하고 IBI와 legacy I2C device를 함께 관리하는 전체 흐름을 설명합니다.

Official pathhardware/peripherals/i3c.html
Source filehardware/peripherals/i3c.rst
Source versionZephyr 3.7.0
TranslationFull · reviewed

본문 기준: Zephyr 3.7.0 LTS3 · 이후 유지보수 패치는 별도입니다.

Part 1

요약·해설

Controller 초기화에서 RSTDAA·DISEC·SETDASA·ENTDAA·ENEC 순서를 거쳐 descriptor 기반 통신으로 이어지는 구조를 다룹니다.

Addressing

static · dynamic · 96-bit reg

Events

IBI · hot-join · ENEC/DISEC

Compatibility

native I3C · legacy I2C

Part 2

접을 수 있는 영어 원문 전체

영어 원문 전체 펼치기
원문 SHA-256 DA6DE40B1C4AFD4C4C4199071A26F89144ED68A40592ADF42509A80B78A952FF
.. _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

Part 3

한국어 전문 번역

I3C(Improved Inter-Integrated Circuit)는 두 signal을 공유하는 peripheral interface bus입니다. Bus의 device는 transaction을 시작하고 clock을 제어하는 ‘controller’, 또는 transaction command에 응답하는 ‘target’ 역할로 동작할 수 있습니다.

현재 API는 I3C Specification 1.1.1을 기준으로 합니다.

I3C Controller API

I3C controller가 bus, 특히 start·stop condition과 clock을 제어할 때 Zephyr I3C controller API를 사용합니다. Sensor 같은 I3C target device와 상호 작용하는 가장 일반적인 mode입니다.

I3C bus에는 전원을 켤 때 address가 없는 device가 있을 수 있습니다. 따라서 controller가 dynamic address assignment를 추가로 수행해야 하며, device 상태를 추적하는 별도의 자료 구조도 유지해야 합니다. Build time에 I3C와 I2C device descriptor array를 만드는 예는 다음과 같습니다.

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);

I3C_DEVICE_ARRAY_DT_INST와 I3C_I2C_DEVICE_ARRAY_DT_INST는 I3C controller 아래 devicetree node에 대응하는 device descriptor array를 만드는 helper macro입니다.

Device driver 초기화 함수 안에서 I3C controller와 I3C bus를 초기화하는 일반적인 순서는 다음과 같습니다.

  1. I3C controller driver instance의 자료 구조를 초기화합니다. DEVICE_DT_INST_DEFINE 같은 일반적인 device 정의 macro를 사용하고, 그 macro의 parameter로 초기화 함수를 넘길 수 있습니다.
    • i3c_addr_slots와 i3c_dev_list는 address assignment와 device list 관리를 돕는 구조체입니다. 이를 사용한다면 i3c_addr_slots_init()을 호출해 초기화해야 하며, 여러 helper function과 함께 사용할 수 있습니다.
    • Controller driver가 필요로 하는 device descriptor도 초기화합니다.
  2. Hardware를 초기화합니다. 다음 작업을 포함하되 여기에만 한정되지는 않습니다.
    • Pin mux와 direction을 설정합니다.
    • Controller clock을 설정합니다.
    • Hardware 전원을 켭니다.
    • SCL clock frequency 같은 hardware 항목을 구성합니다.
  3. Bus initialization을 수행합니다. i3c_bus_init() helper는 아래 순서를 수행합니다. 특별한 처리가 필요 없는 controller는 이 함수를 사용할 수 있습니다.
    1. RSTDAA로 연결된 device의 dynamic address를 reset합니다. Power-on처럼 기존에 배정된 address가 bookkeeping 자료 구조에 기록되지 않은 상황이 있을 수 있으므로, address를 reset하고 새로 할당하는 편이 좋습니다.
    2. DISEC으로 device event를 모두 disable합니다.
    3. 원한다면 SETDASA로 static address를 dynamic address로 사용합니다.
      • 연결된 모든 device가 static address를 dynamic address로 할당하는 SETAASA를 지원하는 것은 아닙니다.
      • I3C target device descriptor의 관련 field를 채우려면 BCR과 DCR을 별도로 얻어야 합니다.
    4. Address가 없는 device가 남아 있다면 ENTDAA로 dynamic address assignment를 시작합니다.
      • Address를 기다리는 device는 Provisioned ID, BCR, DCR을 돌려줍니다. 수신한 Provisioned ID를 등록된 I3C device list와 대조합니다.
        • 일치하는 device가 있으면, SETDASA를 수행하지 않은 경우 명시된 static address를 쓰거나 빈 address를 골라 할당합니다. Device descriptor의 BCR·DCR field도 설정합니다.
        • 일치하는 device가 없으면 policy에 따라 빈 address를 할당하거나, driver가 assignment를 중단하고 error를 반환할 수 있습니다.
        • I3C API는 동작에 device descriptor가 필요합니다. Descriptor가 없는 device에는 API로 접근할 수 없습니다.
      • DAA가 필요한 연결 device가 없으면 이 단계는 생략할 수 있습니다.
    5. 다음 단계는 선택 사항이지만 강력히 권장됩니다.
      • GETMRL과 GETMWL로 최대 read/write length를 가져옵니다.
      • GETMXDS로 최대 read/write speed와 최대 read turnaround time을 가져옵니다.
      • i3c_bus_init() helper는 BCR, DCR, MRL, MWL 같은 기본 device 정보를 가져옵니다.
    6. ENEC으로 device event를 다시 enable합니다.
      • i3c_bus_init() helper는 hot-join event만 다시 enable합니다. IBI event는 해당 device의 IBI를 enable할 때만 enable해야 합니다.

In-Band Interrupt (IBI)

Target device가 In-Band Interrupt(IBI)를 생성할 수 있다면 controller가 이를 인식하도록 준비해야 합니다.

  • i3c_ibi_enable()은 target device의 IBI를 enable합니다.
    • 일부 controller hardware는 특정 target에서 들어오는 IBI를 인식하도록 program해야 하는 IBI slot을 갖습니다.
    • Hardware에 IBI slot이 있다면 i3c_ibi_enable()이 그 slot을 program해야 합니다.
    • Controller의 IBI slot 수는 보통 제한되어 있으므로 enable 작업이 실패할 수 있습니다.
    • Driver 구현은 해당 target device의 interrupt를 enable하는 ENEC command도 보내야 합니다.
  • i3c_ibi_disable()은 target device의 IBI를 disable합니다.
    • Controller hardware가 IBI slot을 사용한다면 slot에서 해당 target device description을 제거합니다.
    • Driver 구현은 해당 target device의 interrupt를 disable하는 DISEC command도 보내야 합니다.

Device Tree

다음은 devicetree에서 I3C controller를 정의하는 예입니다.

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

I3C device의 reg property에는 다음 3개 element가 있습니다.

  • 첫 번째 element는 device의 static address입니다.
    • Static address를 사용하지 않으면 0일 수 있으며, 이 경우 DAA(Dynamic Address Assignment) 중 address를 할당합니다.
    • 0이 아니고 assigned-address property가 설정되지 않았다면 SETDASA(Set Dynamic Address from Static Address)를 실행한 뒤 이 값이 device address가 됩니다.
  • 두 번째 element는 48-bit Provisioned ID(PID)의 bit 33~47, 즉 manufacturer ID를 1-bit left shift한 값을 담는 상위 16-bit입니다.
  • 세 번째 element는 Provisioned ID의 하위 32-bit입니다. Part ID를 16-bit left shift한 PID bit 16~31과 instance ID를 12-bit left shift한 PID bit 12~15를 조합합니다.

@ 뒤의 unit-address는 각 element를 32-bit integer로 취급해 합친 96-bit integer인 reg property 전체와 일치해야 합니다. Devicetree macro를 올바르게 생성하려면 반드시 지켜야 합니다.

I2C Devices

I3C bus 아래에서 동작하도록 driver가 지원하는 I2C device는 I3C controller의 child node로 기술할 수 있습니다. Driver가 I2C controller에서만 동작하도록 작성됐다면 아래에서 설명하는 I2C virtual controller 아래에 node를 정의합니다. 그 밖의 경우 I3C device와 마찬가지로 `reg` property는 3개 element를 가집니다.

  • 첫 번째 element는 device의 static address입니다. I2C device는 dynamic address assignment를 지원하지 않으므로 유효한 address여야 합니다.
  • 두 번째 element는 항상 0입니다. 여러 helper macro가 이 값으로 devicetree entry가 I2C device인지 판별합니다.
  • 세 번째 element는 LVR(Legacy Virtual Register)입니다.
    • bit[31:8]은 사용하지 않습니다.
    • bit[7:5]는 I2C device index입니다.
      • Index 0: I3C device에 50 ns spike filter가 있어 SCL의 high frequency 영향을 받지 않습니다.
      • Index 1: I2C device에 50 ns spike filter가 없지만 SCL high frequency에서 동작할 수 있습니다.
      • Index 2: I3C device에 50 ns spike filter가 없고 SCL high frequency에서 동작할 수 없습니다.
    • bit[4]는 I2C mode indicator입니다. 0은 FM+ mode, 1은 FM mode입니다.

I3C device와 마찬가지로 unit-address는 각 element를 32-bit integer로 취급해 합친 96-bit integer인 reg property 전체와 일치해야 합니다.

Device Drivers for I3C Devices

I3C controller API의 모든 transfer function은 i3c_device_desc device descriptor를 요구합니다. 이 구조체에는 I3C device의 dynamic address, BCR, DCR, MRL, MWL 같은 runtime 정보가 들어 있습니다.

따라서 I3C device driver는 i3c_device_find()를 호출해 controller에서 이 descriptor pointer를 가져와야 합니다. 이 함수는 matching에 사용할 i3c_device_id type의 ID parameter를 받습니다. 반환된 pointer는 이후 controller API 호출에 사용합니다.

I2C Devices under I3C Bus

I3C는 I2C와 backward compatible하므로 controller driver가 I2C API를 구현하면 I3C controller API가 수정 없이 I2C API 호출을 수용할 수 있습니다. 기존 I2C device driver를 고치지 않고 사용할 수 있다는 장점이 있습니다.

그러나 I3C controller API는 device descriptor를 사용하므로 I2C API를 호출할 때마다 I2C device address에 대응하는 descriptor를 찾아야 합니다. 이 lookup 때문에 I2C API 호출에 약간의 처리 비용이 추가됩니다.

다른 방법으로 device driver를 확장해 I3C controller API의 native I2C device 지원을 사용할 수 있습니다. Device 초기화 중 i3c_i2c_device_find()를 호출해 descriptor pointer를 얻고 이후 API 호출에 사용합니다.

어느 방법을 쓰든 I2C device의 devicetree node는 I3C standard에 따라 선언해야 합니다.

I2C virtual controller driver를 사용하면 I3C bus의 I2C device에 연결된 기존 driver를 수정 없이 그대로 사용할 수 있습니다. 이를 위해 devicetree에 중간 node를 추가합니다.

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

관련 configuration option은 다음과 같습니다.

  • CONFIG_I3C
  • CONFIG_I3C_USE_GROUP_ADDR
  • CONFIG_I3C_USE_IBI
  • CONFIG_I3C_IBI_MAX_PAYLOAD_SIZE
  • CONFIG_I3C_CONTROLLER_INIT_PRIORITY

API Reference

i3c_interface, i3c_ccc, i3c_addresses, i3c_target_device Doxygen group이 I3C interface, CCC, address, target device API의 공식 reference를 제공합니다.

Source

출처

영어 원문에는 문단, 문서 지시문, 표, 코드, 코드 심벌, 경로를 그대로 보존했습니다.