I2C Use

Introduction

There are 4 on-chip I2C controllers in Firefly-RK3128 development board, whose configuration and usage will be introduced below.In order to config I2C, there are mainly two steps:

  • Define and register the I2C device.

  • Define and register the I2C driver.

Let’s take the lt8641ex for example.

Define and Register the I2C Device

You need an i2c_client struct to describe the i2c device. However this struct do not need to defined directly. The only thing you need to do is providing the device information and the kernel will build one from it. You can write the information of the I2C device as a node in the dts file, which is shown below:

&i2c0 {
    status = "okay";
    lt8641ex@3f {
        compatible = "firefly,lt8641ex";
        gpio-sw = <&gpio7 GPIO_B2 GPIO_ACTIVE_LOW>;
        reg = <0x3f>;
    };
};

Define and Register the I2C Driver

Define the I2C Driver

Before defining i2_driver, you need to define of_device_id and i2c_device_id first. of_device_id is defined to match the corresponding node in the dts file:

static const struct of_device_id of_rk_lt8641ex_match[] = { { .compatible = "firefly,lt8641ex" },{ /* Sentinel */ }};

define i2c_device_id:

static const struct i2c_device_id lt8641ex_id[] = {{ lt8641ex, 0 },{ }};MODULE_DEVICE_TABLE(i2c, lt8641ex_id);

Define i2c_driver as following:

static struct i2c_driver lt8641ex_device_driver = { 
    .driver     = { 
        .name   = "lt8641ex",
        .owner  = THIS_MODULE,
        .of_match_table = of_rk_lt8641ex_match,
    },  
    .probe      = lt8641ex_probe,
    .remove     = lt8641ex_remove,
    .suspend        = lt8641ex_suspend,
    .resume         = lt8641ex_resume,
    .id_table       = lt8641ex_id,};

NOTE: id_table shows which device the driver support.

Register the I2C Driver

Use i2c_add_driver to register I2C driver.

i2c_add_driver(&lt8641ex_device_driver);

During the call to i2c_add_driver to register I2C driver, all the I2C devices will be traversed. Once matched, the probe function of the driver will be executed.

Send and receive the data via I2C

Once the I2 C driver has been registered, the I2C communication can be carried out.

  • Send the message to slave

static int i2c_master_reg8_send(const struct i2c_client *client, const char reg, const char *buf, int count, int scl_rate) {
    struct i2c_adapter *adap=client->adapter;
    struct i2c_msg msg;int ret;
    char *tx_buf = (char *)kzalloc(count + 1, GFP_KERNEL);
    if(!tx_buf)
    	return -ENOMEM;
    tx_buf[0] = reg;
    memcpy(tx_buf+1, buf, count);  
    msg.addr = client->addr;
    msg.flags = client->flags;
    msg.len = count + 1;
    msg.buf = (char *)tx_buf;
    msg.scl_rate = scl_rate; 
    ret = i2c_transfer(adap, &msg, 1); 
    kfree(tx_buf);
    return (ret == 1) ? count : ret; 
}
  • Read the message from slave

static int i2c_master_reg8_recv(const struct i2c_client *client, const char reg, char *buf, int count, int scl_rate) {
    struct i2c_adapter *adap=client->adapter;
    struct i2c_msg msgs[2];
    int ret;
    char reg_buf = reg; 
    msgs[0].addr = client->addr;
    msgs[0].flags = client->flags;
    msgs[0].len = 1;
    msgs[0].buf = &reg_buf;
    msgs[0].scl_rate = scl_rate; 
    msgs[1].addr = client->addr;
    msgs[1].flags = client->flags | I2C_M_RD;msgs[1].len = count;
    msgs[1].buf = (char *)buf;
    msgs[1].scl_rate = scl_rate; 
    ret = i2c_transfer(adap, msgs, 2);  
    return (ret == 2)? count : ret;
}

Note: msgs[0] is the command that the host sends to the client, telling what info the host needs.msgs[1] is the data received from the client.So far, the host can use i2c_master_reg8_send and i2c_master_reg8_recv to communcate with the I2C client.

  • Communication ExampleThe host sends command to LT8641EX, telling it to use channel 1:

int channel=1;i2c_master_reg8_send(g_lt8641ex->client, 0x00, &channel,1, 100000);

Note: the channel register address is 0x00.The host sends command to LT8641EX, to read the channel currently in use:

u8 ch = 0xfe;i2c_master_reg8_recv(g_lt8641ex->client, 0x00, &ch,1, 100000);

Note: ch is used to store the data, which is read from the I2C client.