Linux device platform driver I2C driver
i2c hardware features and reading and writing processes
- protocol
- Two-wire bidirectional serial transmission, data line
SDA
, clock lineSCL
;SDA
big-endian transmission, each time transmitting one byte (8bit) - The read and write register address
addr
is divided into a fixed part and a programmable part. You need to query the chipdatasheet
- Read and write timing
- Start transmission (
SCL=1
,SDA
changes from high to low) - End transmission (
SCL=1
,SDA
changes from low to high) - Transmission response: After the master sends 8 bit data, it will wait for
ACK
at the ninth clock and receiveSDA=0
, otherwiseSDA=1
- Data transmission (
SCL=1
,SDA
stable, no jump) - Data change (
SCL=0
,SDA
changes the transmitted bit)
- Start transmission (
- Two-wire bidirectional serial transmission, data line
- Reading and writing process
- Write
- The master initiates Start transmission signal
- The master issues i2c addr (7bit) and W operation (1bit), waiting for
ACK
- slave sends
ACK
- The master sends reg add(8bit) and waits for
ACK
- slave sends
ACK
- The master sends reg data(8bit) and waits for
ACK
- slave sends
ACK
- Master initiates End transmission signal
- Read
- The master initiates Start transmission signal
- The master issues i2c addr (7bit) and W operations (1bit), waiting for
ACK
- slave sends
ACK
- The master sends reg add(8bit) and waits for
ACK
- slave sends
ACK
- The master initiates the end of transmission signal
- Master initiates transmission signal
- The master issues i2c addr (7bit) and R operations (1bit), waiting for
ACK
- slave sends
ACK
- The slave sends reg data(8bit) and waits until
ACK
- master initiates
ACK
- Write
Main steps required to write I2C client driver
- 1. Declare the device ID supported by the driver, you can use the
i2c_device_id
statement- If device tree is supported,
of_device_id
can be used
static const struct i2c_device_id myi2c_ids[] = {<!-- --> {<!-- -->"myi2c", 0}, {<!-- -->}, }; \t //For the open firmware (OF) matching mechanism of the device tree, use of_device_id static const struct of_device_id of_myi2c_id[] = {<!-- --> {<!-- -->.compatible = "buff,myi2c",}, {<!-- -->}, };
- If device tree is supported,
- 2. Call
MODULE_DEVICE_TABLE(i2c, myi2c_ids)
to register the device list with the I2C kernel device- If Device Tree is supported,
MODULE_DEVICE_TABLE(of, of_lm3643_id)
must be used
MODULE_DEVICE_TABLE(i2c, myi2c_ids); MODULE_DEVICE_TABLE(of, of_myi2c_id);
- If Device Tree is supported,
- 3. Write the
probe
andremove
functionsaccording to actual needs static int myi2c_probe(struct i2c_client *client, const struct i2c_device_id *id) {<!-- --> xxx } static int myi2c_remove(struct i2c_client *client) {<!-- --> xxx }
- 4. Declare and fill in
struct i2c_driver
, and set theid_table
id array (lm3643_ids
) >, fill in other variablesstruct i2c_driver my_i2c_driver = {<!-- --> .driver = {<!-- --> .name = MYI2C_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(of_myi2c_id), }, .probe = myi2c_probe, .remove = myi2c_remove, .id_table = myi2c_ids, };
- 5. Use the filled
struct i2c_driver
to call themodule_i2c_driver
functionmodule_i2c_driver( & amp;my_i2c_driver);
- Or use the traditional driver initialization function
static int __init myi2c_init(void) {<!-- --> xxx //Core function i2c_add_driver( & amp;my_i2c_driver); xxx } static void __exit myi2c_exit(void) {<!-- --> xxx i2c_del_driver( & amp;my_i2c_driver); xxx } module_init(myi2c_init); //i2c_add_driver module_exit(myi2c_exit); //i2c_del_driver
i2c client driver read and write interface implementation
According to the reading and writing process above, there are differences in reading and writing of I2C client
struct myi2c_dev_reg {<!-- --> u32 dev_id; /*!< Device ID */ u8 addr; /*!< Register DRAM address */ u32 data; /*!< Register Value */ }; int myi2c_read_data(struct i2c_client *client, struct myi2c_dev_reg *reg) {<!-- --> unsigned char buf[2]; struct i2c_msg msg[2]; int rval = 0; //smbus can use the i2c bus protocol for signal transmission, but not vice versa. if (client->adapter->algo->master_xfer != NULL) {<!-- --> msg[0].addr = client->addr; msg[0].buf = & amp;reg->addr; msg[0].len = sizeof(reg->addr); msg[0].flags = 0; msg[1].addr = client->addr; msg[1].buf = buf; msg[1].len = sizeof(buf); msg[1].flags = I2C_M_RD; rval = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); if (ARRAY_SIZE(msg) == rval) {<!-- --> rval = buf[1]; reg->data = (rval << 8) | buf[0]; } } else if (client->adapter->algo->smbus_xfer != NULL) {<!-- --> rval = i2c_smbus_read_word_data(client, reg->addr); } else {<!-- --> rval = -EOPNOTSUPP; } return rval; } int myi2c_write_data(struct i2c_client *client, struct myi2c_dev_reg *reg) {<!-- --> struct i2c_msg msg; char msg_count = 1; unsigned char buf[3]; int rval = 0; if (client->adapter->algo->master_xfer != NULL) {<!-- --> //i2c can only transmit 8 bits of data at a time, so it can only be cut according to the length of 8 bits buf[0] = reg->addr; buf[1] = reg->data & 0xff; buf[2] = (reg->data >> 8) & amp; 0xff; msg.addr = client->addr; msg.buf = buf; msg.len = sizeof(buf); msg.flags = 0; rval = i2c_transfer(client->adapter, & amp;msg, msg_count); if (msg_count == rval) {<!-- --> rval = 0; } } else if (client->adapter->algo->smbus_xfer != NULL) {<!-- --> rval = i2c_smbus_write_word_data(client, reg->addr, reg->data); } else {<!-- --> rval = -EOPNOTSUPP; } return rval; }