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