[linux/driver]I2C client driver implementation

Linux device platform driver I2C driver

i2c hardware features and reading and writing processes

  • protocol
    • Two-wire bidirectional serial transmission, data line SDA, clock line SCL; 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 chip datasheet
    • 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 receive SDA=0, otherwise SDA=1
      • Data transmission (SCL=1, SDA stable, no jump)
      • Data change (SCL=0, SDA changes the transmitted bit)
  • 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

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",},
    {<!-- -->},
    };
    
  • 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);
    
  • 3. Write the probe and remove functions
     according 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 the id_tableid array (lm3643_ids) >, fill in other variables
    struct 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 the module_i2c_driver function
    module_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;
}