driver-platform-i3c-des.md 15.8 KB
Newer Older
A
Annie_wang 已提交
1
# I3C
A
annie_wangli 已提交
2

A
Annie_wang 已提交
3
## Introduction
A
annie_wangli 已提交
4

A
Annie_wang 已提交
5
### Function
A
annie_wangli 已提交
6

A
Annie_wang 已提交
7
Improved 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

A
Annie_wang 已提交
9
I3C 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

A
Annie_wang 已提交
11 12 13 14 15
The I3C driver APIs provide a set of common functions for I3C transfer, including:
- Opening and closing an I3C controller
- Obtaining and setting I3C controller parameters
- Performing custom I3C message transfer by using a message array
- Requesting and releasing an IBI
16

A
Annie_wang 已提交
17
### Basic Concepts
A
annie_wangli 已提交
18

A
Annie_wang 已提交
19 20 21 22 23
- IBI
  
  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.
  
- Dynamic Address Assignment (DAA)
24

A
Annie_wang 已提交
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
  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:

  - The device has an I2C compliant static address that can be used by the host.
  - 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.

- Common Command Code (CCC)

  All I3C devices support CCC. The CCC can be sent to a specific I3C target device or all I3C target devices.

- Bus Characteristic Register (BCR)

  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.

- Device Characteristic Register (DCR)

  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.

### Working Principles

In 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.

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.

**Figure 1** I3C physical connection 
A
annie_wangli 已提交
51
![](figures/I3C_physical_connection.png "I3C_physical_connection")
A
annie_wangli 已提交
52

A
Annie_wang 已提交
53 54 55 56 57 58 59 60 61 62 63 64 65
### Constraints

Currently, the I3C module supports only the kernels (LiteOS) of mini and small systems.

## Usage Guidelines

### When to Use

I3C can connect to one or more I3C or I2C target devices. It is used to:
- Communicate with sensors, such as gyroscopes, barometers, and image sensors that support the I3C protocol.
- Communicate with devices with other ports (such as UART serial ports) through software or hardware protocols.

### Available APIs
A
annie_wangli 已提交
66 67 68

**Table 1** I3C driver APIs

A
Annie_wang 已提交
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87

| API       | Description             |
| ------------- | ----------------- |
| I3cOpen       | Opens an I3C controller.    |
| I3cClose      | Closes an I3C controller.    |
| I3cTransfer   | Performs custom transfer.       |
| I3cSetConfig  | Sets the I3C controller.    |
| I3cGetConfig  | Obtains the I3C controller configuration. |
| I3cRequestIbi | Requests an IBI.     |
| I3cFreeIbi    | Releases an IBI.     |

>![](../public_sys-resources/icon-note.gif) **NOTE**<br> 
>All APIs described in this document can be called only in kernel mode.

### How to Develop

The figure below illustrates the use of I3C driver APIs.

**Figure 2** Process of using I3C driver APIs 
A
annie_wangli 已提交
88 89
![](figures/I3C_usage_flowchart.png "I3C_usage_flowchart")

A
Annie_wang 已提交
90
#### Opening an I3C Controller
A
annie_wangli 已提交
91

A
Annie_wang 已提交
92
Before I3C communication, call **I3cOpen()** to open an I3C controller.
A
annie_wangli 已提交
93 94 95 96 97 98
```c
DevHandle I3cOpen(int16_t number);
```

**Table 2** Description of I3cOpen

A
Annie_wang 已提交
99 100 101 102 103 104 105 106
| Name      | Description           |
| ---------- | ------------------- |
| number     | I3C controller number. |
| **Return Value**| **Description**     |
| NULL       | The operation failed.  |
| Controller handle| The operation is successful. The handle of the I3C controller opened is returned. |

Example: Open I3C controller 1 of the eight I3C controllers numbered from 0 to 7 in the system.
A
annie_wangli 已提交
107 108 109 110 111 112 113 114 115 116 117 118

```c
DevHandle i3cHandle = NULL; /* I3C controller handle. /

/* Open I3C controller 1. */
i3cHandle = I3cOpen(1);
if (i3cHandle == NULL) {
    HDF_LOGE("I3cOpen: failed\n");
    return;
}
```

A
Annie_wang 已提交
119
#### Performing I3C Communication
A
annie_wangli 已提交
120 121 122 123 124 125 126 127

Call **I3cTransfer()** to transfer messages.
```c
int32_t I3cTransfer(DevHandle handle, struct I3cMsg *msgs, int16_t count, enum TransMode mode);
```

**Table 3** Description of I3cTransfer

A
Annie_wang 已提交
128 129 130 131 132 133 134 135 136 137 138 139

| Name      | Description                                    |
| ---------- | -------------------------------------------- |
| handle     | I3C controller handle.                               |
| msgs       | Pointer to the message array of the data to transfer.                  |
| count      | Length of the message array.                                |
| mode       | Transmission mode. The value **0** indicates I2C mode, **1** indicates I3C mode, and **2** indicates CCC transmission. |
| **Return Value**| **Description**                              |
| Positive integer    | The operation is successful. The number of message structures that are successfully transmitted is returned.                    |
| Negative value      | The operation failed.                                    |

The 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.
A
annie_wangli 已提交
140 141 142 143 144 145

```c
int32_t ret;
uint8_t wbuff[2] = { 0x12, 0x13 };
uint8_t rbuff[2] = { 0 };
struct I3cMsg msgs[2]; /* Custom message array for transfer. */
A
annie_wangli 已提交
146
msgs[0].buf = wbuff;    /* Data to write. */
A
annie_wangli 已提交
147 148
msgs[0].len = 2;        /* Length of the data to write. */
msgs[0].addr = 0x3F; /* Address of the device to which the data is written. */
A
annie_wangli 已提交
149 150
msgs[0].flags = 0;      /* Transfer flag. A write operation is performed by default. */
msgs[1].buf = rbuff;    /* Data to read. */
A
annie_wangli 已提交
151 152 153 154 155 156 157 158 159 160 161
msgs[1].len = 2;        /* Length of the data to read. */
msgs[1].addr = 0x3F;    /* Address of the device from which the data is read. */
msgs[1].flags = I3C_FLAG_READ /* I3C_FLAG_READ is set. */
/* Transfer two messages in I2C mode. */
ret = I3cTransfer(i3cHandle, msgs, 2, I2C_MODE);
if (ret != 2) {
    HDF_LOGE("I3cTransfer: failed, ret %d\n", ret);
    return;
}
```

A
Annie_wang 已提交
162
>![](./public_sys-resources/icon-caution.gif) **Caution**<br>
A
annie_wangli 已提交
163
>-   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**.
A
annie_wangli 已提交
164
>-   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.
A
annie_wangli 已提交
165 166
>-   Using **I3cTransfer()** may cause the system to sleep. Do not call it in the interrupt context.

A
Annie_wang 已提交
167 168 169
#### Obtaining the I3C Controller Configuration

Call **I3cGetConfig()** to obtain the configuration of an I3C controller.
A
annie_wangli 已提交
170 171 172 173 174 175 176

```c
int32_t I3cGetConfig(DevHandle handle, struct I3cConfig *config);
```

**Table 4** Description of I3cGetConfig

A
Annie_wang 已提交
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200

| Name      | Description      |
| ---------- | -------------- |
| handle     | I3C controller handle. |
| config     | Pointer to the I3C controller configuration. |
| **Return Value**| **Description**|
| 0          | The operation is successful.      |
| Negative value      | The operation failed.      |

The following is an example of obtaining the I3C controller configuration:

```c
struct I3cConfig config;

ret = I3cGetConfig(i3cHandle, &config);
if (ret != HDF_SUCCESS) {
    HDF_LOGE("%s: Get config fail!", __func__);
    return HDF_FAILURE;
}
```

#### Setting an I3C Controller

Call **I3cSetConfig()** to set an I3C controller.
A
annie_wangli 已提交
201 202 203 204 205

```c
int32_t I3cSetConfig(DevHandle handle, struct I3cConfig *config);
```

206
**Table 5** Description of I3cSetConfig
A
annie_wangli 已提交
207

A
Annie_wang 已提交
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233

| Name      | Description      |
| ---------- | -------------- |
| handle     | I3C controller handle. |
| config     | Pointer to the I3C controller configuration. |
| **Return Value**| **Description**|
| 0          | The operation is successful.      |
| Negative value      | The operation failed.      |

The following is an example of setting an I3C controller:

```c
struct I3cConfig config;

config->busMode = I3C_BUS_HDR_MODE;
config->curMaster = NULL;
ret = I3cSetConfig(i3cHandle, &config);
if (ret != HDF_SUCCESS) {
    HDF_LOGE("%s: Set config fail!", __func__);
    return HDF_FAILURE;
}
```

#### Requesting an IBI

Call **I3cRequestIbi()** to request an IBI.
A
annie_wangli 已提交
234 235 236 237 238

```c
int32_t I3cRequestIbi(DevHandle handle, uint16_t addr, I3cIbiFunc func, uint32_t payload);
```

239
**Table 6** Description of I3cRequestIbi
A
annie_wangli 已提交
240

A
Annie_wang 已提交
241 242 243 244 245 246 247 248 249 250 251 252

| Name      | Description      |
| ---------- | -------------- |
| handle     | I3C controller handle. |
| addr       | I3C device address.   |
| func       | Callback used to return the IBI.   |
| payload    | IBI payload.   |
| **Return Value**| **Description**|
| 0          | The operation is successful.      |
| Negative value      | The operation failed.      |

The following is an example:
A
annie_wangli 已提交
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268

```c
static int32_t TestI3cIbiFunc(DevHandle handle, uint16_t addr, struct I3cIbiData data)
{
    (void)handle;
    (void)addr;
    HDF_LOGD("%s: %.16s", __func__, (char *)data.buf);

    return 0;
}

int32_t I3cTestRequestIbi(void)
{
    DevHandle i3cHandle = NULL;
    int32_t ret;

A
Annie_wang 已提交
269
    /* Open the I3C controller. */
A
annie_wangli 已提交
270 271 272 273 274 275 276
    i3cHandle = I3cOpen(1);
    if (i3cHandle == NULL) {
        HDF_LOGE("I3cOpen: failed\n");
    return;
}
    ret = I3cRequestIbi(i3cHandle, 0x3F, TestI3cIbiFunc, 16);
    if (ret != 0) {
A
Annie_wang 已提交
277
        HDF_LOGE("%s: Request IBI failed!", __func__);
A
annie_wangli 已提交
278 279 280 281 282 283 284 285 286 287
        return -1;
    }

    I3cClose(i3cHandle);
    HDF_LOGD("%s: Done", __func__);

    return 0;
}
```

A
Annie_wang 已提交
288 289 290
#### Releasing an IBI

Call **I3cFreeIbi()** to release an IBI.
A
annie_wangli 已提交
291 292 293 294 295

```c
int32_t I3cFreeIbi(DevHandle handle, uint16_t addr);
```

296
**Table 7** Description of I3cFreeIbi
A
annie_wangli 已提交
297

A
Annie_wang 已提交
298 299 300 301 302 303 304 305 306 307

| Name      | Description      |
| ---------- | -------------- |
| handle     | I3C controller handle. |
| addr       | I3C device address.   |
| **Return Value**| **Description**|
| 0          | The operation is successful.      |
| Negative value      | The operation failed.      |

The following is an example:
A
annie_wangli 已提交
308 309 310 311 312

```c
I3cFreeIbi(i3cHandle, 0x3F); /* Release an IBI. */
```

A
Annie_wang 已提交
313
#### Closing an I3C Controller
A
annie_wangli 已提交
314 315 316 317 318 319

Call **I3cClose()** to close the I3C controller after the communication is complete.
```c
void I3cClose(DevHandle handle); 
```

320
**Table 8** Description of I3cClose
A
annie_wangli 已提交
321 322


A
Annie_wang 已提交
323 324 325
| Name      | Description      |
| ---------- | -------------- |
| handle     | I3C controller handle. |
A
annie_wangli 已提交
326

A
Annie_wang 已提交
327
The following is an example:
A
annie_wangli 已提交
328 329

```c
A
Annie_wang 已提交
330
I3cClose(i3cHandle); /* Close the I3C controller. */
A
annie_wangli 已提交
331 332
```

A
Annie_wang 已提交
333
## Development Example
A
annie_wangli 已提交
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352

This following example shows how to use I3C APIs to manage an I3C device on a Hi3516D V300 development board.

Because 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:

-   SoC: Hi3516D V300

-   Virtual: The I3C address is 0x3f, and the register bit width is 1 byte.

-   The virtual I3C devices are connected to virtual I3C controllers 18 and 19.

Perform simple I3C transfer to test whether the I3C channels are normal.

The sample code is as follows:

```c
#include "i3c_if.h"          /* Header file for I3C standard APIs */
#include "hdf_log.h"         /* Header file for log APIs */
##include "osal_io.h"         /* Header file for I/O read and write APIs */
A
Annie_wang 已提交
353
#include "osal_time.h"       /* Header file for delay and sleep APIs */
A
annie_wangli 已提交
354 355 356 357 358 359 360 361 362

/* Define a device structure to hold information. */
struct TestI3cDevice {
    uint16_t busNum;              /* I3C bus number */ 
    uint16_t addr;                /* I3C device address */ 
    uint16_t regLen;              /* Register bit width */
    DevHandle i3cHandle;          /* I3C controller handle */ 
};

A
Annie_wang 已提交
363
/* Use I3cTransfer() to encapsulate a register read/write helper function. Use flag to indicate a read or write operation. */
A
annie_wangli 已提交
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
static int TestI3cReadWrite(struct TestI3cDevice *testDevice, unsigned int regAddr,
    unsigned char *regData, unsigned int dataLen, uint8_t flag)
{
    int index = 0;
    unsigned char regBuf[4] = {0};
    struct I3cMsg msgs[2] = {0};

    /* Perform length adaptation for the single- or dual-byte register. */
    if (testDevice->regLen == 1) { 
        regBuf[index++] = regAddr & 0xFF;
    } else {
        regBuf[index++] = (regAddr >> 8) & 0xFF;
        regBuf[index++] = regAddr & 0xFF;
    }

    /* Fill in the I3cMsg message structure. */
    msgs[0].addr = testDevice->addr;
    msgs[0].flags = 0; /* The flag 0 indicates a write operation. */
    msgs[0].len = testDevice->regLen;
    msgs[0].buf = regBuf;

    msgs[1].addr = testDevice->addr;
A
Annie_wang 已提交
386
    msgs[1].flags = (flag == 1) ? I3C_FLAG_READ : 0; /* Add the read flag. */
A
annie_wangli 已提交
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
    msgs[1].len = dataLen;
    msgs[1].buf = regData;

    if (I3cTransfer(testDevice->i3cHandle, msgs, 2, I2C_MODE) != 2) {
        HDF_LOGE("%s: i3c read err", __func__);
        return HDF_FAILURE;
    }
    return HDF_SUCCESS;
}

/* Read data from the register. */
static inline int TestI3cReadReg(struct TestI3cDevice *testDevice, unsigned int regAddr,
    unsigned char *regData, unsigned int dataLen)
{
    return TestI3cReadWrite(testDevice, regAddr, regData, dataLen, 1);
}

/* Write data to the register. */
static inline int TestI3cWriteReg(struct TestI3cDevice *testDevice, unsigned int regAddr,
    unsigned char *regData, unsigned int dataLen)
{
    return TestI3cReadWrite(testDevice, regAddr, regData, dataLen, 0);
}

/* Main entry of I3C routines. */
static int32_t TestCaseI3c(void)
{
    int32_t i;
    int32_t ret;
    unsigned char bufWrite[7] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xA, 0xB, 0xC };
    unsigned char bufRead[7] = {0};
    static struct TestI3cDevice testDevice;

    /* Initialize device information. */
    testDevice.busNum = 18;
    testDevice.addr = 0x3F;
    testDevice.regLen = 1;
    testDevice.i3cHandle = NULL;

    /* Open an I3C controller. */
    testDevice.i3cHandle = I3cOpen(testDevice.busNum);
    if (testDevice.i3cHandle == NULL) {
        HDF_LOGE("%s: Open I3c:%u fail!", __func__, testDevice.busNum);
        return -1;
    }

    /* Write 7-byte data continuously to the device whose address is 0x3F. */
    ret = TestI3cWriteReg(&testDevice, 0x3F, bufWrite, 7);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: test i3c write reg fail!:%d", __func__, ret);
        I3cClose(testDevice.i3cHandle);
        return -1;
    }
    OsalMSleep(10);

    /* Read 7-byte data continuously from the device whose address is 0x3F. */
    ret = TestI3cReadReg(&testDevice, 0x3F, bufRead, 7);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: test i3c read reg fail!:%d", __func__, ret);
        I3cClose(testDevice.i3cHandle);
        return -1;
    }
    HDF_LOGI("%s: test i3c write&read reg success!", __func__);

    /* Close the I3C controller. */
    I3cClose(testDevice.i3cHandle);

    return 0;
}
```