• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# I3C
2
3## Introduction
4
5### Function
6
7Improved Inter-Integrated Circuit (I3C) is a simple and cost-efficient two-wire bidirectional synchronous serial bus protocol developed by the Mobile Industry Processor Interface (MIPI) Alliance.
8
9I3C is a two-wire bidirectional serial bus, optimized for multiple sensor target devices and controlled by only one I3C controller at a time. It is backward compatible with Inter-Integrated circuit (I2C) target devices, but features higher speed and lower power consumption. Moreover, I3C supports in-band interrupts (IBIs), hot-joins of target devices, and controller switchover. The IBIs over the serial bus eliminates the need for an extra interrupt line to complete interrupts in I2C. I2C devices, I3C target devices, and the I3C secondary controller can co-exist on the same I3C bus.
10
11The I3C module provides a set of common APIs for I3C transfer, including:
12- Opening and closing an I3C controller
13- Obtaining and setting I3C controller parameters
14- Performing custom I3C message transfer by using a message array
15- Requesting and releasing an IBI
16
17### Basic Concepts
18
19- IBI
20
21  When there is no start signal on the serial clock (SCL) line, the I3C target device can pull down the serial data (SDA) line to make the controller send an SCL start signal, which initiates an IBI request. If multiple target devices send interrupt requests at the same time, the I3C controller arbitrates the requests based on the target device addresses. The request with a lower address is responded first.
22
23- Dynamic Address Assignment (DAA)
24
25  The I3C controller can dynamically allocate addresses to target devices to avoid address conflicts. Before addresses are allocated, each I3C device connected to a I3C bus must be uniquely identified in either of the following ways:
26    1) The device has an I2C compliant static address that can be used by the host.
27    2) The device has a 48-bit temporary ID. The host must use a 48-bit temporary ID unless the device has a static IP address.
28
29- Common Command Code (CCC)
30
31  All I3C devices support CCC. The CCC can be sent to a specific I3C target device or all I3C target devices.
32
33- Bus Characteristic Register (BCR)
34
35  Each I3C device connected to an I3C bus has a read-only BCR, which describes the I3C compliant device's role and capabilities for use in DAA and CCC.
36
37- Device Characteristic Register (DCR)
38
39  Each I3C device connected to an I3C bus has a read-only DCR, which describes the I3C compliant device type (such as accelerometers, gyroscope, and others) for use in DAA and DCC.
40
41### Working Principles
42
43In the Hardware Driver Foundation (HDF), the I3C module uses the unified service mode for API adaptation. In this mode, a service is used as the I3C manager to handle external access requests in a unified manner. The unified service mode applies when the system has multiple device objects of the same type, for example, when there are more than 10  I3C controllers. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed.
44
45Compared with I2C, I3C features higher speed and lower power consumption, supports IBIs, hot-joins of target devices, and controller switchover. I3C is also backward compatible with I2C target devices. Multiple devices, such as I2C target device, I3C target device, and I3C secondary controller, can be connected to an I3C bus. However, the I3C bus must have only one controller.
46
47**Figure 1** I3C physical connection
48
49![](figures/I3C_physical_connection.png "I3C_physical_connection")
50
51### Constraints
52
53The I3C module supports only the kernel (LiteOS-A) for mini and small systems and cannot be used in user mode.
54
55## Usage Guidelines
56
57### When to Use
58
59I3C can connect to one or more I3C or I2C target devices. It is used to:
60
61- Communicate with sensors, such as gyroscopes, barometers, and image sensors that support the I3C protocol.
62- Communicate with devices with other ports (such as UART serial ports) through software or hardware protocols.
63
64### Available APIs
65
66The following table describes the APIs provided by the I3C module. For more information about the APIs, see **//drivers/hdf_core/framework/include/platform/i3c_if.h**.
67
68**Table 1** I3C driver APIs
69
70| API       | Description         |
71| ------------- | ----------------- |
72| DevHandle I3cOpen(int16_t number)       | Opens an I3C controller.    |
73| void I3cClose(DevHandle handle)      | Closes an I3C controller.    |
74| int32_t I3cTransfer(DevHandle handle, struct I3cMsg \*msg, int16_t count, enum TransMode mode)   | Performs custom transfer.       |
75| int32_t I3cSetConfig(DevHandle handle, struct I3cConfig \*config)  | Sets the I3C controller.    |
76| int32_t I3cGetConfig(DevHandle handle, struct I3cConfig \*config)  | Obtains I3C controller configuration.|
77| int32_t I3cRequestIbi(DevHandle handle, uint16_t addr, I3cIbiFunc func, uint32_t payload) | Requests an IBI.     |
78| int32_t I3cFreeIbi(DevHandle handle, uint16_t addr)    | Releases an IBI.     |
79
80>![](../public_sys-resources/icon-note.gif) **NOTE**
81>
82>All APIs described in this document can be called only in kernel mode.
83
84### How to Develop
85
86The following figure illustrates how to use the I3C APIs.
87
88**Figure 2** Process of using I3C driver APIs
89
90![](figures/using-I3C-process.png)
91
92#### Opening an I3C Controller
93
94Before I3C communication, call **I3cOpen()** to open an I3C controller.
95```c
96DevHandle I3cOpen(int16_t number);
97```
98
99**Table 2** Description of I3cOpen
100
101| Name      | Description           |
102| ---------- | ------------------- |
103| number     | I3C controller number.        |
104| **Return Value**| **Description**     |
105| NULL       | The operation fails.  |
106| Controller handle| The operation is successful. The handle of the I3C controller opened is returned.|
107
108Example: Open I3C controller 1 of the eight I3C controllers numbered from 0 to 7 in the system.
109
110```c
111DevHandle i3cHandle = NULL; /* I3C controller handle. /
112
113/* Open I3C controller 1. */
114i3cHandle = I3cOpen(1);
115if (i3cHandle == NULL) {
116    HDF_LOGE("I3cOpen: failed\n");
117    return;
118}
119```
120
121#### Obtaining the I3C Controller Configuration
122
123```c
124int32_t I3cGetConfig(DevHandle handle, struct I3cConfig *config);
125```
126
127**Table 3** Description of I3cGetConfig
128
129| Name      | Description      |
130| ---------- | -------------- |
131| handle     | I3C controller handle. |
132| config     | Pointer to the I3C controller configuration. |
133| **Return Value**| **Description**|
134| 0          | The operation is successful.      |
135| Negative value      | The operation fails.      |
136
137The following is an example of obtaining the I3C controller configuration:
138
139```c
140struct I3cConfig config;
141
142ret = I3cGetConfig(i3cHandle, &config);
143if (ret != HDF_SUCCESS) {
144    HDF_LOGE("%s: Get config fail!", __func__);
145    return HDF_FAILURE;
146}
147```
148
149#### Setting an I3C Controller
150
151```c
152int32_t I3cSetConfig(DevHandle handle, struct I3cConfig *config);
153```
154
155**Table 4** Description of I3cSetConfig
156
157| Name      | Description      |
158| ---------- | -------------- |
159| handle     | I3C controller handle. |
160| config     | Pointer to the I3C controller configuration. |
161| **Return Value**| **Description**|
162| 0          | The operation is successful.      |
163| Negative value      | The operation fails.      |
164
165The following is an example of setting an I3C controller:
166
167```c
168struct I3cConfig config;
169
170config->busMode = I3C_BUS_HDR_MODE;
171config->curMaster = NULL;
172ret = I3cSetConfig(i3cHandle, &config);
173if (ret != HDF_SUCCESS) {
174    HDF_LOGE("%s: Set config fail!", __func__);
175    return HDF_FAILURE;
176}
177```
178
179#### Performing I3C Communication
180
181Call **I3cTransfer()** to transfer messages.
182```c
183int32_t I3cTransfer(DevHandle handle, struct I3cMsg *msgs, int16_t count, enum TransMode mode);
184```
185
186**Table 5** Description of I3cTransfer
187
188| Name      | Description                                    |
189| ---------- | -------------------------------------------- |
190| handle     | I3C controller handle.                               |
191| msgs       | Pointer to the message array of the data to transfer.                  |
192| count      | Length of the message array.                                |
193| mode       | Transmission mode. The value **0** indicates I2C mode, **1** indicates I3C mode, and **2** indicates CCC transmission.|
194| **Return Value**| **Description**                              |
195| Positive integer    | The operation is successful. The number of message structures that are successfully transmitted is returned.                    |
196| Negative value      | The operation fails.                                    |
197
198The I3C messages are of the I3cMsg type. Each message structure indicates a read or write operation. A message array can be used to perform multiple read or write operations.
199
200```c
201int32_t ret;
202uint8_t wbuff[2] = { 0x12, 0x13 };
203uint8_t rbuff[2] = { 0 };
204struct I3cMsg msgs[2];  /* Custom message array for transfer. */
205msgs[0].buf = wbuff;    /* Data to write. */
206msgs[0].len = 2;        /* Length of the data to write. */
207msgs[0].addr = 0x3F;    /* Address of the device to which the data is written. */
208msgs[0].flags = 0;      /* Transfer flag. A write operation is performed by default. */
209msgs[1].buf = rbuff;    /* Data to read. */
210msgs[1].len = 2;        /* Length of the data to read. */
211msgs[1].addr = 0x3F;    /* Address of the device from which the data is read. */
212msgs[1].flags = I3C_FLAG_READ /* I3C_FLAG_READ is set. */
213/* Transfer two messages in I2C mode. */
214ret = I3cTransfer(i3cHandle, msgs, 2, I2C_MODE);
215if (ret != 2) {
216    HDF_LOGE("I3cTransfer: failed, ret %d\n", ret);
217    return;
218}
219```
220
221>![](./public_sys-resources/icon-caution.gif) **Caution**<br>
222>-   The device address in the **I3cMsg** structure does not contain the read/write flag bit. The read/write information is passed by the read/write control bit in **flags**.
223>-   The I3C controller determines the maximum number of messages to transfer at a time and the maximum length of each message.
224>-   Using **I3cTransfer()** may cause the system to sleep. Do not call it in the interrupt context.
225
226#### Requesting an IBI
227
228```c
229int32_t I3cRequestIbi(DevHandle handle, uint16_t addr, I3cIbiFunc func, uint32_t payload);
230```
231
232**Table 6** Description of I3cRequestIbi
233
234| Name      | Description      |
235| ---------- | -------------- |
236| handle     | I3C controller handle. |
237| addr       | I3C device address.   |
238| func       | Callback used to return the IBI.   |
239| payload    | IBI payload.   |
240| **Return Value**| **Description**|
241| 0          | The operation is successful.      |
242| Negative value      | The operation fails.      |
243
244The following is an example:
245
246```c
247static int32_t TestI3cIbiFunc(DevHandle handle, uint16_t addr, struct I3cIbiData data)
248{
249    (void)handle;
250    (void)addr;
251    HDF_LOGD("%s: %.16s", __func__, (char *)data.buf);
252
253    return 0;
254}
255
256int32_t I3cTestRequestIbi(void)
257{
258    DevHandle i3cHandle = NULL;
259    int32_t ret;
260
261    /* Open the I3C controller. */
262    i3cHandle = I3cOpen(1);
263    if (i3cHandle == NULL) {
264        HDF_LOGE("I3cOpen: failed\n");
265    return;
266}
267    ret = I3cRequestIbi(i3cHandle, 0x3F, TestI3cIbiFunc, 16);
268    if (ret != 0) {
269        HDF_LOGE("%s: Request IBI failed!", __func__);
270        return -1;
271    }
272
273    I3cClose(i3cHandle);
274    HDF_LOGD("%s: Done", __func__);
275
276    return 0;
277}
278```
279
280#### Releasing an IBI
281
282```c
283int32_t I3cFreeIbi(DevHandle handle, uint16_t addr);
284```
285
286**Table 7** Description of I3cFreeIbi
287
288| Name      | Description      |
289| ---------- | -------------- |
290| handle     | I3C controller handle. |
291| addr       | I3C device address.   |
292| **Return Value**| **Description**|
293| 0          | The operation is successful.      |
294| Negative value      | The operation fails.      |
295
296The following is an example:
297
298```c
299I3cFreeIbi(i3cHandle, 0x3F); /* Release an IBI. */
300```
301
302#### Closing an I3C Controller
303
304Call **I3cClose()** to close the I3C controller after the communication is complete.
305```c
306void I3cClose(DevHandle handle);
307```
308
309**Table 8** Description of I3cClose
310
311| Name      | Description      |
312| ---------- | -------------- |
313| handle     | I3C controller handle. |
314
315The following is an example:
316
317```c
318I3cClose(i3cHandle); /* Close the I3C controller. */
319```
320
321## Example
322
323The following example presents how to use I3C APIs to manage an I3C device on a Hi3516D V300 development board. <br>The basic hardware information is as follows:
324
325-   SoC: Hi3516D V300
326
327-   Virtual I3C device: The I3C address is 0x3f, and the register bit width is 1 byte.
328
329-   The virtual I3C device is connected to I3C controllers 18 and 19.
330
331Perform simple I3C transfer to test whether the I3C channels are normal.
332
333The sample code is as follows:
334
335```c
336#include "i3c_if.h"          /* Header file for I3C standard APIs */
337#include "hdf_log.h"         /* Header file for log APIs */
338##include "osal_io.h"        /* Header file for I/O read and write APIs */
339#include "osal_time.h"       /* Header file for delay and sleep APIs */
340
341/* Define a device structure to hold information. */
342struct TestI3cDevice {
343    uint16_t busNum;              /* I3C bus number */
344    uint16_t addr;                /* I3C device address */
345    uint16_t regLen;              /* Register bit width */
346    DevHandle i3cHandle;          /* I3C controller handle */
347};
348
349/* Use I3cTransfer() to encapsulate a register read/write helper function. Use flag to indicate a read or write operation. */
350static int TestI3cReadWrite(struct TestI3cDevice *testDevice, unsigned int regAddr,
351    unsigned char *regData, unsigned int dataLen, uint8_t flag)
352{
353    int index = 0;
354    unsigned char regBuf[4] = {0};
355    struct I3cMsg msgs[2] = {0};
356
357    /* Perform length adaptation for the single- or dual-byte register. */
358    if (testDevice->regLen == 1) {
359        regBuf[index++] = regAddr & 0xFF;
360    } else {
361        regBuf[index++] = (regAddr >> 8) & 0xFF;
362        regBuf[index++] = regAddr & 0xFF;
363    }
364
365    /* Fill in the I3cMsg message structure. */
366    msgs[0].addr = testDevice->addr;
367    msgs[0].flags = 0; /* The flag 0 indicates a write operation. */
368    msgs[0].len = testDevice->regLen;
369    msgs[0].buf = regBuf;
370
371    msgs[1].addr = testDevice->addr;
372    msgs[1].flags = (flag == 1) ? I3C_FLAG_READ : 0; /* Add the read flag. */
373    msgs[1].len = dataLen;
374    msgs[1].buf = regData;
375
376    if (I3cTransfer(testDevice->i3cHandle, msgs, 2, I2C_MODE) != 2) {
377        HDF_LOGE("%s: i3c read err", __func__);
378        return HDF_FAILURE;
379    }
380    return HDF_SUCCESS;
381}
382
383/* Read data from the register. */
384static inline int TestI3cReadReg(struct TestI3cDevice *testDevice, unsigned int regAddr,
385    unsigned char *regData, unsigned int dataLen)
386{
387    return TestI3cReadWrite(testDevice, regAddr, regData, dataLen, 1);
388}
389
390/* Write data to the register. */
391static inline int TestI3cWriteReg(struct TestI3cDevice *testDevice, unsigned int regAddr,
392    unsigned char *regData, unsigned int dataLen)
393{
394    return TestI3cReadWrite(testDevice, regAddr, regData, dataLen, 0);
395}
396
397/* Main entry of I3C routines. */
398static int32_t TestCaseI3c(void)
399{
400    int32_t i;
401    int32_t ret;
402    unsigned char bufWrite[7] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xA, 0xB, 0xC };
403    unsigned char bufRead[7] = {0};
404    static struct TestI3cDevice testDevice;
405
406    /* Initialize device information. */
407    testDevice.busNum = 18;
408    testDevice.addr = 0x3F;
409    testDevice.regLen = 1;
410    testDevice.i3cHandle = NULL;
411
412    /* Open an I3C controller. */
413    testDevice.i3cHandle = I3cOpen(testDevice.busNum);
414    if (testDevice.i3cHandle == NULL) {
415        HDF_LOGE("%s: Open I3c:%u fail!", __func__, testDevice.busNum);
416        return -1;
417    }
418
419    /* Write 7-byte data continuously to the device whose address is 0x3F. */
420    ret = TestI3cWriteReg(&testDevice, 0x3F, bufWrite, 7);
421    if (ret != HDF_SUCCESS) {
422        HDF_LOGE("%s: test i3c write reg fail!:%d", __func__, ret);
423        I3cClose(testDevice.i3cHandle);
424        return -1;
425    }
426    OsalMSleep(10);
427
428    /* Read 7-byte data continuously from the device whose address is 0x3F. */
429    ret = TestI3cReadReg(&testDevice, 0x3F, bufRead, 7);
430    if (ret != HDF_SUCCESS) {
431        HDF_LOGE("%s: test i3c read reg fail!:%d", __func__, ret);
432        I3cClose(testDevice.i3cHandle);
433        return -1;
434    }
435    HDF_LOGI("%s: test i3c write&read reg success!", __func__);
436
437    /* Close the I3C controller. */
438    I3cClose(testDevice.i3cHandle);
439
440    return 0;
441}
442```
443