❤ Detailed explanation of IIC driver under Linux

reference material: https://www.yuanzige.com/

brief introduction

I2C is a very common bus protocol. I2C is designed by NXP company. I2C uses two lines to count between the master controller and the slave
According to the communication. One is SCL (serial clock line) and the other is SDA (serial data line). These two data lines need to be connected with pull-up resistors. When the bus is idle, SCL and SDA are at high level. The speed of I2C bus can reach 100Kb/S in standard mode and 400Kb/S in fast mode. I2C bus works according to a certain protocol. Next, let's take a look at I2C protocol.

I2C supports multiple slaves, that is, multiple I2C slaves can be attached to an I2C controller. These different I2C slaves have different device addresses, so that the I2C master controller can access the specified I2C device through the device address of the I2C device. An I2C bus connects multiple I2C devices, as shown in the figure:

In the figure above, SDA and SCL wires must be connected with a pull-up resistor, generally 4.7K. Other I2C slave devices are connected to SDA
And SCL, so that multiple I2C devices can be accessed through SDA and SCL.

Transmit data

1. Start bit
As the name suggests, that is, the I2C communication start flag. Through this start bit, you can tell the I2C slave that "I" will start I2C communication. When SCL is high, the falling edge of SDA is indicated as the start bit.

2. Stop bit
The stop bit is the flag bit to stop I2C communication, which is opposite to the function of the start bit. When the SCL bit is high, the rising edge of SDA indicates the stop bit

Both the start signal and the end signal are sent by the host. After the start signal is generated, the bus is in the occupied state, and after the end signal is generated, the bus is in the idle state

3. Data transmission
During data transmission, I2C bus shall ensure that the data on SDA is stable during SCL high level, so the data change on SDA can only occur during SCL low level, as shown in the following figure:

4. Response signal
After the I2C host sends the 8-bit data, it will set the SDA to the input state and wait for the I2C slave to respond, that is, wait for the I2C slave
Tell the host that it has received data. The response signal is sent by the slave. The host needs to provide the clock required for the response signal, and the host sends it
The clock signal immediately following the 8-bit data is used for the reply signal. The slave sends a response signal by pulling the SDA down
No. indicates that the communication is successful, otherwise it indicates that the communication fails.

Every time the transmitter transmits a byte of data, the sender will wait for a certain time for the receiver's response signal. The receiving end sends a response signal to the transmitting end by pulling down the SDA data line to remind the transmitting end that I have accepted it and the data can continue to be transmitted. Next, the transmitting end can continue to send data.
.
The data length of each frame is 8 bits, but every 8 bits of data sent must be followed by a reply bit, so the data frame length of IIC is 9 bits

5. IIC write timing
Communication is nothing more than two operations, read and write.
(1) . start signal.

(2) Send I2C device address. Each I2C device has a device address. Determine which I2C device to access by sending a specific device address. This is an 8-bit data, in which the upper 7 bits are the device address and the last 1 bit is the read-write bit. If it is 1, it means that it is a read operation and if it is 0, it means that it is a write operation.

(3) I2C device address is followed by a read-write bit. 0 indicates write operation and 1 indicates read operation.
(4) . ACK response signal sent by the slave.
(5) . resend the start signal.
(6) Send the register address of the data to be written.
(7) . ACK response signal sent by the slave.
(8) Send the data to be written to the register.
(9) . ACK response signal sent by the slave.
(10) . stop signal.
6. IIC read timing
I2C single byte read timing is a little more complex than write timing. The read timing is divided into four steps:
Step 1: send the device address,
Step 2: send the register address to be read,
Step 3: resend the device address,
Step 4: I2C outputs the register value to be read from the device. Let's take a specific look at this step.
(1) . the host sends the start signal.
(2) The host sends the I2C slave address to be read.
(3) The read / write control bit is a write signal because it sends data to the I2C slave device.
(4) . ACK response signal sent by the slave.
(5) . resend the START signal.
(6) . the host sends the register address to be read.
(7) . ACK response signal sent by the slave.
(8) . resend the START signal.
(9) . resend the I2C slave address to be read.
(10) Read / write control bit, here is the read signal, indicating that the next step is to read data from the I2C from the device.
(11) . ACK response signal sent by the slave.
(12) Data read from I2C device.
(13) The host sends NO ACK signal, indicating that the reading is completed, and there is no need to send ACK signal from the slave.
(14) The host sends a STOP signal to STOP I2C communication.

How to use IIC under Linux

IIC bus is similar to platform bus, which separates the device from the driver:

Two important data structures are used here: i2c_adapter and i2c_algorithm. The Linux kernel controls the I2C adapter of SOC
(controller) is abstracted into i2c_adapter. The i2c_adapter structure is defined in the include/linux/i2c.h file. The structure content is as follows:

struct i2c_adapter {
 struct module *owner;
 unsigned int class; /* classes to allow probing for */
 const struct i2c_algorithm *algo; /* Bus access algorithm */
 void *algo_data;
 /* data fields that are valid for all devices */
 struct rt_mutex bus_lock;
 int timeout; /* in jiffies */
 int retries;
 struct device dev; /* the adapter device */
 int nr;
 char name[48];
 struct completion dev_released;
 struct mutex userspace_clients_lock;
 struct list_head userspace_clients;
 struct i2c_bus_recovery_info *bus_recovery_info;
 const struct i2c_adapter_quirks *quirks;
 };

There is an i2c_algorithm in the i2c_adapter structure. For an I2C adapter, you must provide external read-write API functions, and the device driver can use these API functions to complete read-write operations. I2c_algorithm is the method of communication between the I2C adapter and the IIC device.

 struct i2c_algorithm {
......
 int (*master_xfer)(struct i2c_adapter *adap,
struct i2c_msg *msgs,
 int num);
 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
 unsigned short flags, char read_write,
 u8 command, int size, union i2c_smbus_data *data);
 /* To determine what the adapter supports */
 u32 (*functionality) (struct i2c_adapter *);
......
 };

master_xfer is the transfer function of I2C adapter, which can be used to complete the communication with IIC devices.
smbus_xfer is the transfer function of SMBUS bus.

To sum up, the main work of I2C bus driver or I2C adapter driver is to initialize i2c_adapter structure variables,
Then set the master_xfer function in i2c_algorithm. After that, use i2c_add_numbered_adapter or
i2c_add_adapter these two functions register the set i2c_adapter with the system. The prototypes of these two functions are as follows:

int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
The difference between the two functions is i2c_add_adapter Use dynamic bus numbers instead of i2c_add_numbered_adapter The static bus number is used. The meaning of function parameters and return values is as follows:
adapter or adap: To add to Linux In kernel i2c_adapter,that is I2C Adapter.
Return value: 0, successful; Negative value, failed.
If you want to delete I2C If the adapter is used i2c_del_adapter The prototype of the function is as follows:
void i2c_del_adapter(struct i2c_adapter * adap)
Function parameters and return values have the following meanings:
adap: To delete I2C Adapter.
Return value: None

Generally, the I2C bus driver of SOC is written by semiconductor manufacturers. For example, the I2C adapter driver NXP of I.MX6U has been written, which does not need to be written by users. Therefore, I2C bus driver is shielded for SoC users. We just need to focus on I2C device driver. Unless you work in a semiconductor company, your job is to write I2C adapter driver.

IIC device driver

I2C device driver focuses on two data structures: i2c_client and i2c_driver, according to the bus, device and driver model, I2C bus has been described in the previous section. There are devices and drivers left, i2c_client describes the device information, i2c_driver describes the driving content, which is similar to platform_driver.

i2c_client structure:

 struct i2c_client {
 unsigned short flags; /* sign */
 unsigned short addr; /* Chip address, 7 bits, low 7 bits*/
......
 char name[I2C_NAME_SIZE]; /* name */
 struct i2c_adapter *adapter; /* Corresponding I2C adapter */
 struct device dev; /* Equipment structure */
 int irq; /* interrupt */
 struct list_head detected;
......
 };

One device corresponds to one i2c_client, every time an I2C device is detected, an I2C device will be assigned to this I2C device_ client.

i2c_driver structure

struct i2c_driver {
1 unsigned int class;
2 int (*attach_adapter)(struct i2c_adapter *) __deprecated;
3 int (*probe)(struct i2c_client *, const struct i2c_device_id *);
4 int (*remove)(struct i2c_client *);
5 void (*shutdown)(struct i2c_client *);
6 void (*alert)(struct i2c_client *, unsigned int data);
7 int (*command)(struct i2c_client *client, unsigned int cmd,void *arg);
8 struct device_driver driver;
9 const struct i2c_device_id *id_table;
10 int (*detect)(struct i2c_client *, struct i2c_board_info *);
11 const unsigned short *address_list;
12 struct list_head clients;
 };

The third function: after the I2C device and driver are successfully matched, the probe function will execute, just like the platform driver.

The 8th structure: device_driver drives the structure. If you use the device tree, you need to set device_driver's
of_match_table member variable, that is, the compatible attribute of the driver.

The 9th structure: id_table is a traditional device matching ID table that does not use the device tree.

For our I2C device driver writers, the key work is to build i2c_driver, after the build is completed, you need to register this I2C with the Linux kernel_ driver. i2c_ The driver registration function is int i2c_register_driver, the prototype of this function is as follows:

int i2c_register_driver(struct module *owner,struct i2c_driver *driver)
Function parameters and return values have the following meanings:
owner:  Generally THIS_MODULE. 
driver: To register i2c_driver. 
Return value: 0, successful; Negative value, failed.

In addition, i2c_add_driver is also often used to register i2c_driver, i2c_add_driver is a macro defined as follows:

 #define i2c_add_driver(driver) \
 i2c_register_driver(THIS_MODULE, driver)

i2c_add_driver is right i2c_register_driver makes a simple package with only one parameter, i.e. I2C to be registered_ driver.
When you log off the I2C device driver, you need to register the I2C previously_ Driver logs out of the Linux kernel and needs i2c_del_driver function. The prototype of this function is as follows:

void i2c_del_driver(struct i2c_driver *driver)
Function parameters and return values have the following meanings:
driver: To log off i2c_driver. 
Return value: none.

i2c_ The registration example code of driver is as follows:

 /* i2c Driven probe function */
 static int xxx_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
 /* Function specific program */
 return 0;
 }
/* i2c Driven remove function */
 static int ap3216c_remove(struct i2c_client *client)
 {
 /* Function specific program */
 return 0;
 }

 /* Traditional matching method ID list */
 static const struct i2c_device_id xxx_id[] = {
 	{"xxx", 0},
 	{}
 };

 /* Device tree matching list */
 static const struct of_device_id xxx_of_match[] = {
 { .compatible = "xxx" },
 { /* Sentinel */ }
 };

 /* i2c Drive structure */
 static struct i2c_driver xxx_driver = {
 .probe = xxx_probe,
 .remove = xxx_remove,
 .driver = {
 .owner = THIS_MODULE,
 .name = "xxx",
 .of_match_table = xxx_of_match,
 },
 .id_table = xxx_id,
 };

 /* Drive entry function */
 static int __init xxx_init(void)
 {
 int ret = 0;

 ret = i2c_add_driver(&xxx_driver);
 return ret;
 }
 /* Drive exit function */
 static void __exit xxx_exit(void)
{
 i2c_del_driver(&xxx_driver);
 }
 module_init(xxx_init);
 module_exit(xxx_exit);

It's basically the platform

I2C device and drive matching process

The matching process between I2C device and driver is completed by I2C core. Drivers / I2C / I2C core. C is the core of I2C
The core provides some API functions independent of specific hardware, such as the above:
1, i2c_adapter register / unregister function

int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
void i2c_del_adapter(struct i2c_adapter * adap)

2, i2c_driver registration / logoff function

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
int i2c_add_driver (struct i2c_driver *driver)
void i2c_del_driver(struct i2c_driver *driver)

The matching process between device and driver is also completed by I2C bus. The data structure of I2C bus is i2c_bus_type, defined in
Drivers / I2C / I2C core. C file, I2C_ bus_ The content of type is as follows:

 struct bus_type i2c_bus_type = {
 .name = "i2c",
 .match = i2c_device_match,
 .probe = i2c_device_probe,
 .remove = i2c_device_remove,
 .shutdown = i2c_device_shutdown,
 };

. match is the device and driver matching function of I2C bus, here is i2c_device_match is a function. The contents of this function are as follows:

static int i2c_device_match(struct device *dev, struct,device_driver *drv)
{
 struct i2c_client *client = i2c_verify_client(dev);
 struct i2c_driver *driver;

 if (!client)
 return 0;

 /* Attempt an OF style match
of_driver_match_device Function is used to complete device tree device and driver matching. Compare the compatible attribute of the I2C device node with the 
of_device_id Whether the compatible attribute in is equal. If so, it indicates that the I2C device and driver match
 */
 if (of_driver_match_device(dev, drv))
 return 1;

 /* Then ACPI style match 
acpi_driver_match_device The function is used for matching in ACPI form
*/
 if (acpi_driver_match_device(dev, drv))
 return 1;
 driver = to_i2c_driver(drv);
 
 /* match on an id table if there is one */
 if (driver->id_table)
 
 /*
 i2c_match_id The function is used for the traditional I2C device and driver matching process without device tree. Compare I2C
 Device name and I2C_ device_ Whether the name field of ID is equal. If it is equal, it indicates that the I2C device and driver match.
*/

 return i2c_match_id(driver->id_table, client) != NULL;

 return 0;
 }

*I2C adapter driver analysis

This chapter is not important and can be skipped!!!!
I2C adapter driver is the I2C controller driver of SOC. I2C device drivers need to be written by users according to different I2C devices,
The I2C adapter driver is generally written by SOC manufacturers. For example, NXP has written the I2C adapter driver of I.MX6U. stay
Find the I2C1 controller node of I.MX6U in imx6ull.dtsi file. The node content is as follows:

i2c1: i2c@021a0000 {
 #address-cells = <1>;
 #size-cells = <0>;
 compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
 reg = <0x021a0000 0x4000>;
 interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
 clocks = <&clks IMX6UL_CLK_I2C1>;
 status = "disabled";
 }

Focus on the compatible attribute value of i2c1 node, because the corresponding value can be found in the Linux source code
Driver file for. Here, the i2c1 node has two compatible attribute values: "fsl,imx6ul-i2c" and "fsl,imx21-i2c", which are in the Linux source code
Search these two strings to find the corresponding driver file. 1. The I2C adapter driver file of mx6u is
Drivers / I2C / busses / I2C IMX. C, which contains the following contents:

 static struct platform_device_id imx_i2c_devtype[] = {
{
 .name = "imx1-i2c",
 .driver_data = (kernel_ulong_t)&imx1_i2c_hwdata,
 }, {
 .name = "imx21-i2c",
 .driver_data = (kernel_ulong_t)&imx21_i2c_hwdata,
 }, {
 /* sentinel */
	 }
 };
 MODULE_DEVICE_TABLE(platform, imx_i2c_devtype);

 static const struct of_device_id i2c_imx_dt_ids[] = {
 { .compatible = "fsl,imx1-i2c", .data = &imx1_i2c_hwdata, },
 { .compatible = "fsl,imx21-i2c", .data = &imx21_i2c_hwdata, },//compatible attribute matching in dts
 { .compatible = "fsl,vf610-i2c", .data = &vf610_i2c_hwdata, },
 { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids);
......
 static struct platform_driver i2c_imx_driver = {
 .probe = i2c_imx_probe,//probe function to obtain hardware resource information and information in dts
 .remove = i2c_imx_remove,
 .driver = {
 .name = DRIVER_NAME,
 .owner = THIS_MODULE,
 .of_match_table = i2c_imx_dt_ids,
 .pm = IMX_I2C_PM,
 	},
 .id_table = imx_i2c_devtype,
 };

 static int __init i2c_adap_imx_init(void)
 {
 return platform_driver_register(&i2c_imx_driver);
 }
 subsys_initcall(i2c_adap_imx_init);

 static void __exit i2c_adap_imx_exit(void)
 {
 platform_driver_unregister(&i2c_imx_driver);
 }
 module_exit(i2c_adap_imx_exit);

probe function:

 static int i2c_imx_probe(struct platform_device *pdev)
 {
 const struct of_device_id *of_id =
 of_match_device(i2c_imx_dt_ids, &pdev->dev);
 struct imx_i2c_struct *i2c_imx;
 struct resource *res;
 struct imxi2c_platform_data *pdata =dev_get_platdata(&pdev->dev);
 void __iomem *base;
 int irq, ret;
 dma_addr_t phy_addr;

 dev_dbg(&pdev->dev, "<%s>\n", __func__);

 irq = platform_get_irq(pdev, 0);//Call platform_ get_ The IRQ function gets the interrupt number.
......
/*
Call platform_ get_ The resource function obtains the physical base address of the I2C1 controller register from the device tree, that is, 0X021A0000. Get
 Use devm after register base address_ ioremap_ The resource function maps the memory to get the virtual address that can be used in the Linux kernel.
*/
 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//This method does not use dts method, but only platform
 base = devm_ioremap_resource(&pdev->dev, res);


 if (IS_ERR(base))
 return PTR_ERR(base);

 phy_addr = (dma_addr_t)res->start;
 
//NXP uses imx_i2c_struct structure to represent I2C controller of I.MX series SOC. Devm is used here_ Kzalloc function to request memory.
 i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx),GFP_KERNEL);
 if (!i2c_imx)
 return -ENOMEM;
 if (of_id)
 i2c_imx->hwdata = of_id->data;
 else
 i2c_imx->hwdata = (struct imx_i2c_hwdata *)
 platform_get_device_id(pdev)->driver_data;

 /* Setup i2c_imx driver structure */
 strlcpy(i2c_imx->adapter.name, pdev->name,sizeof(i2c_imx->adapter.name));
 //imx_ i2c_ The struct structure should have a member variable called adapter, which is i2c_adapter, where I2C is initialized_ adapter. 
 i2c_imx->adapter.owner = THIS_MODULE;
 i2c_imx->adapter.algo = &i2c_imx_algo;//Set I2C_ The algo member variable of adapter is i2c_imx_algo, that is, set i2c_algorithm. 
 i2c_imx->adapter.dev.parent = &pdev->dev;
 i2c_imx->adapter.nr = pdev->id;
 i2c_imx->adapter.dev.of_node = pdev->dev.of_node;
 i2c_imx->base = base;

 /* Get I2C clock */
 i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
......
 ret = clk_prepare_enable(i2c_imx->clk);
......
 /* Request IRQ   Register the I2C controller interrupt, and the interrupt service function is i2c_imx_isr. */
 ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr,IRQF_NO_SUSPEND, pdev->name, i2c_imx);
......
 /* Init queue */
 init_waitqueue_head(&i2c_imx->queue);

 /* Set up adapter data */
 i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);

 /* Set up clock divider Set I2C frequency to IMX by default_ I2C_ BIT_ Rate = 100kHz, if the device tree node is set to
 If the "clock frequency" attribute is set, the I2C frequency will use the clock frequency attribute value. */
 i2c_imx->bitrate = IMX_I2C_BIT_RATE;
 ret = of_property_read_u32(pdev->dev.of_node,"clock-frequency", &i2c_imx->bitrate);
 
 if (ret < 0 && pdata && pdata->bitrate)
 i2c_imx->bitrate = pdata->bitrate;
 /* Set up chip registers to defaults Set I2CR and I2SR registers controlled by I2C1 */
 imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,i2c_imx, IMX_I2C_I2CR);
 imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx,IMX_I2C_I2SR);
 
 /* Add I2C adapter Call I2C_ add_ numbered_ The adapter function registers I2C with the Linux kernel_ adapter. */
 ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
 if (ret < 0) {
 dev_err(&pdev->dev, "registration failed\n");
 goto clk_disable;
 }

 /* Set up platform driver data */
 platform_set_drvdata(pdev, i2c_imx);
 clk_disable_unprepare(i2c_imx->clk);
......
 /* Init DMA config if supported  Apply for DMA */
 i2c_imx_dma_request(i2c_imx, phy_addr);

 return 0; /* Return OK */

 clk_disable:
 clk_disable_unprepare(i2c_imx->clk);
 return ret;
 }

i2c_ imx_ The probe function mainly works on the following two points:
① . initialize i2c_adapter, setting I2C_ The algorithm is i2c_imx_algo, and finally register with the Linux kernel
i2c_adapter.
② Initialize the relevant registers of I2C1 controller.
i2c_imx_algo contains the communication function master between I2C1 adapter and I2C device_ xfer, i2c_imx_algo structure determination
The meaning is as follows:

static struct i2c_algorithm i2c_imx_algo = {
 .master_xfer = i2c_imx_xfer,
 .functionality = i2c_imx_func,
 };

. functionality, which is used to return the communication protocol supported by this I2C adapter. Here, functionality is
i2c_imx_func function, i2c_imx_func function is as follows:

static u32 i2c_imx_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_READ_BLOCK_DATA;
}

i2c_imx_xfer function, because this function is used to complete the communication with I2C device. The contents of this function are as follows (omitted):

static int i2c_imx_xfer(struct i2c_adapter *adapter,
 struct i2c_msg *msgs, int num)
 {
 unsigned int i, temp;
 int result;
 bool is_lastmsg = false;
 struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
 dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);

 /* Start I2C transfer Call I2C_ imx_ The start function turns on I2C communication. */
 result = i2c_imx_start(i2c_imx);
 if (result)
 goto fail0;

 /* read/write data */
 for (i = 0; i < num; i++) {
 if (i == num - 1)
 is_lastmsg = true;

 if (i) {
 dev_dbg(&i2c_imx->adapter.dev, "<%s> repeated start\n", __func__);
 temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
 temp |= I2CR_RSTA;
 imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
 result = i2c_imx_bus_busy(i2c_imx, 1);
 if (result)
 goto fail0;
 }
 dev_dbg(&i2c_imx->adapter.dev,
 "<%s> transfer message: %d\n", __func__, i);
 /* write/read data */
......
 if (msgs[i].flags & I2C_M_RD)
 result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg);//If you are reading data from an I2C device, call i2c_imx_read function.
 /*
Write data to I2C device. If you want to use DMA, use i2c_imx_dma_write function to
 Finish writing data. If you don't use DMA, use I2C_ imx_ The write function completes writing data.
 */
 else {
 if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD)
 result = i2c_imx_dma_write(i2c_imx, &msgs[i]);
 else
 result = i2c_imx_write(i2c_imx, &msgs[i]);
 }
 if (result)
 goto fail0;
 }

 fail0:
 /* Stop I2C transfer */
 i2c_imx_stop(i2c_imx);//After I2C communication completes, call i2c_. imx_ The stop function stops I2C communication

 dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n",__func__,
 (result < 0) ? "error" : "success msg",
 (result < 0) ? result : num);
 return (result < 0) ? result : num;
 }

I2C device driver writing process

If you don't use the device tree, no records will be made here

Using device tree mode

 &i2c1 {
 clock-frequency = <100000>;
 pinctrl-names = "default";
 pinctrl-0 = <&pinctrl_i2c1>;
 status = "okay";
 mag3110@0e {
 compatible = "fsl,mag3110";
 reg = <0x0e>;
 position = <2>;
 };
......
 };

I2C equipment data transceiver processing flow
The first thing an I2C device driver needs to do is initialize i2c_driver and register with the Linux kernel. When the device and driver match i2c_driver
The probe function will be executed. What the probe function does is the character device driver. It is generally required to write in the probe
To initialize the I2C device, you must be able to read and write the I2C device register, which is used here
i2c_ The transfer function. i2c_ The transfer function will eventually call I2C in the I2C adapter_ Master in algorithm_ Xfer letter
Number, I2C for I.MX6U_ imx_ Xfer this function. i2c_ The prototype of transfer function is as follows:

int i2c_transfer(struct i2c_adapter *adap,struct i2c_msg *msgs,int num)
Function parameters and return values have the following meanings:
adap:  Used I2C Adapter, i2c_client Its corresponding is saved i2c_adapter. 
msgs:  I2C One or more messages to send.
num:  Number of messages, i.e msgs Number of.
Return value: negative value, failed, other non negative values, sent msgs quantity

Let's focus on the parameter msgs, which is an i2c_msg type pointer parameter. I2C sends and receives data. In short, it is message transmission. The Linux kernel uses i2c_msg structure to describe a message. i2c_msg structure definition
In the include/uapi/linux/i2c.h file, the structure is as follows:

 struct i2c_msg {
 __u16 addr; /* Slave address */
 __u16 flags; /* sign */
 #define I2C_M_TEN 0x0010
 #define I2C_M_RD 0x0001
 #define I2C_M_STOP 0x8000
 #define I2C_M_NOSTART 0x4000
 #define I2C_M_REV_DIR_ADDR 0x2000
 #define I2C_M_IGNORE_NAK 0x1000
 #define I2C_M_NO_RD_ACK 0x0800
 #define I2C_M_RECV_LEN 0x0400
 __u16 len; /* Message (msg) length */
 __u8 *buf; /* Message data */
 };

Using I2C_ The transfer function should construct I2C before sending data_ MSG, using i2c_transfer to receive I2C data
The example code of the message is as follows:

 /* Equipment structure */
 struct xxx_dev {
 ......
 /*
Device structure. Add a pointer member variable private that executes void to the device structure_ Data, this member variable is used to save the private number of the device
 According to. In the I2C device driver, we generally point it to the I2C corresponding to the I2C device_ client. 
 */
 
 void *private_data; /* Private data, usually set to i2c_client */
 };

/*
 * @description : Read multiple register data of I2C device
 * @param – dev : I2C equipment
 * @param – reg : First register address to read
 * @param – val : Read data
 * @param – len : Length of data to read
 * @return : Operation results
xxx_read_regs The function is used to read multiple register data of I2C device. Line 18 defines an i2c_msg array, 2 array elements, because 
I2C When reading data, you should first send the register address to be read, and then read the data, so you need to prepare two i2c_msg.  One for sending mail
 Memory address, which is used to read the register value. For msg[0], set flags to 0 to write data. The addr of msg[0] is an I2C device
 The buf member variable of msg[0] is the register address to be read. For msg[1], set flags to I2C_M_RD for read
 Fetch data. The buf member variable of msg[1] is used to save the read data, and the len member variable is the length of the data to be read. Call i2c_transfer 
Function to complete I2C data reading operation.
 */
 static int xxx_read_regs(struct xxx_dev *dev, u8 reg, void *val,int len)
 {
 int ret;
 struct i2c_msg msg[2];
 struct i2c_client *client = (struct i2c_client *)dev->private_data;
 /* msg[0],The first write message sends the first address of the register to be read */
 msg[0].addr = client->addr; /* I2C Device address */
 msg[0].flags = 0; /* Mark as send data */
 msg[0].buf = &reg; /* First address read */
 msg[0].len = 1; /* reg length */

 /* msg[1],The second read message reads the register data */
 msg[1].addr = client->addr; /* I2C Device address */
 msg[1].flags = I2C_M_RD; /* Mark as read data */
 msg[1].buf = val; /* Read data buffer */
 msg[1].len = len; /* Length of data to read */
 ret = i2c_transfer(client->adapter, msg, 2);
 if(ret == 2) {
 ret = 0;
 } else {
 ret = -EREMOTEIO;
 }
 return ret;
 }

 /*
 * @description : Write data to multiple registers of I2C device
 * @param – dev : Device structure to write
 * @param – reg : First address of register to be written
 * @param – val : Data buffer to write to
 * @param – len : Length of data to write
 * @return : Operation results
 xxx_write_regs Function is used to write data to multiple registers of I2C device. I2C write operation is a little simpler than read operation, so an i2c_msg is
 Yes. Array b is used to store the first address of the register and the data to be sent, and set the addr of msg as the I2C device address. Set the flags of msg to 
 0,That is, write data. Set the data to be sent, that is, array b. Set len of msg to len+1, because one byte of register ground is added
 Address. Finally, through I2C_ The transfer function completes the write operation to the I2C device
 
 */
 static s32 xxx_write_regs(struct xxx_dev *dev, u8 reg, u8 *buf,u8 len)
 {
 u8 b[256];
 struct i2c_msg msg;
 struct i2c_client *client = (struct i2c_client *)dev->private_data;

 b[0] = reg; /* Register header address */
 memcpy(&b[1],buf,len); /* Copy the data to be sent to array b */

 msg.addr = client->addr; /* I2C Device address */
 msg.flags = 0; /* Mark as write data */

 msg.buf = b; /* Data buffer to send */
 msg.len = len + 1; /* Length of data to send */

 return i2c_transfer(client->adapter, &msg, 1);
 }

In addition, there are two API functions respectively used for I2C data sending and receiving operations. These two functions will eventually call i2c_transfer.
Let's first look at the I2C data sending function I2C_ master_ The prototype of send function is as follows:

int i2c_master_send(const struct i2c_client *client,const char *buf,int count)
Function parameters and return values have the following meanings:
client:  I2C Equipment corresponding i2c_client. 
buf: Data to send.
count:  The number of data bytes to be sent should be less than 64 KB,think i2c_msg of len The member variable is a u16(nothing
 Symbol 16 bit)Type of data.
Return value: negative value, failure, other non negative values, number of bytes sent.

The I2C data receiving function is i2c_master_recv, the function prototype is as follows:

int i2c_master_recv(const struct i2c_client *client,char *buf,int count)
Function parameters and return values have the following meanings:
client:  I2C Equipment corresponding i2c_client. 
buf: Data to receive.
count:  The number of data bytes to be received should be less than 64 KB,think i2c_msg of len The member variable is a u16(Unsigned 16 bit)Type of data.
Return value: negative value, failure, other non negative values, number of bytes sent.

Examples of punctual atoms

dts:

&i2c1 {
 clock-frequency = <100000>;
 pinctrl-names = "default";
 pinctrl-0 = <&pinctrl_i2c1>;
 status = "okay";
 
mag3110@0e {
 compatible = "fsl,mag3110";
 reg = <0x0e>;
 position = <2>;
 };

 fxls8471@1e {
 compatible = "fsl,fxls8471";
 reg = <0x1e>;
 position = <0>;
 interrupt-parent = <&gpio5>;
 interrupts = <0 8>;
 };
 };

driver:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "ap3216creg.h"
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
file name 		:  ap3216c.c
 author 	  	:  Zuo Zhongkai
 edition 	   	:  V1.0
 describe 	   	:  AP3216C driver
 other 	   	:  nothing
 Forum 	   	:  www.openedv.com
 journal 	   	:  First version v1.0 created by Zuo Zhongkai on September 2, 2019
***************************************************************/
#define AP3216C_CNT	1
#define AP3216C_NAME	"ap3216c"
/*
ap3216c Equipment structure, private_ The data member variable is used to store the I2C corresponding to ap3216c_ client.  ir, als, and on line 40 
ps The IR, ALS and PS data of AP3216C are stored respectively.
*/
struct ap3216c_dev {
	dev_t devid;			/* Equipment number 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;	/* class 		*/
	struct device *device;	/* equipment 	 */
	struct device_node	*nd; /* Device node */
	int major;			/* Main equipment No */
	void *private_data;	/* Private data */
	unsigned short ir, als, ps;		/* Three optical sensor data */
};

static struct ap3216c_dev ap3216cdev;//Define an ap3216c_ Device structure variable ap3216cdev of type dev.

/*
 * @description	: Read multiple register data from ap3216c
 * @param - dev:  ap3216c equipment
 * @param - reg:  First register address to read
 * @param - val:  Read data
 * @param - len:  Length of data to read
 * @return 		: Operation results
 
ap3216c_read_regs Function to read multiple bytes, but AP3216C does not seem to support continuous multiple words
 Section reading. This function can realize continuous reading of multiple bytes when testing other I2C devices, but it is not available on AP3216C
 It can read multiple bytes continuously. But there is no problem reading a byte.
*/
static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len)
{
	int ret;
	struct i2c_msg msg[2];
	struct i2c_client *client = (struct i2c_client *)dev->private_data;

	/* msg[0]Send the first address to read for */
	msg[0].addr = client->addr;			/* ap3216c address */
	msg[0].flags = 0;					/* Mark as send data */
	msg[0].buf = &reg;					/* First address read */
	msg[0].len = 1;						/* reg length*/

	/* msg[1]Read data */
	msg[1].addr = client->addr;			/* ap3216c address */
	msg[1].flags = I2C_M_RD;			/* Mark as read data*/
	msg[1].buf = val;					/* Read data buffer */
	msg[1].len = len;					/* Length of data to read*/

	ret = i2c_transfer(client->adapter, msg, 2);
	if(ret == 2) {
		ret = 0;
	} else {
		printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);
		ret = -EREMOTEIO;
	}
	return ret;
}

/*
 * @description	: Write data to ap3216c multiple registers
 * @param - dev:  ap3216c equipment
 * @param - reg:  First address of register to be written
 * @param - val:  Data buffer to write to
 * @param - len:  Length of data to write
 * @return 	  :   Operation results
 
 ap3216c_write_regs Function to implement continuous multi byte write operations.
 
 */
static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, u8 len)
{
	u8 b[256];
	struct i2c_msg msg;
	struct i2c_client *client = (struct i2c_client *)dev->private_data;
	
	b[0] = reg;					/* Register header address */
	memcpy(&b[1],buf,len);		/* Copy the data to be written into array b */
		
	msg.addr = client->addr;	/* ap3216c address */
	msg.flags = 0;				/* Mark as write data */

	msg.buf = b;				/* Data buffer to write to */
	msg.len = len + 1;			/* Length of data to write */

	return i2c_transfer(client->adapter, &msg, 1);
}

/*
 * @description	: Read ap3216c the specified register value and read a register
 * @param - dev:  ap3216c equipment
 * @param - reg:  Register to read
 * @return 	  :   Read register value
 
 ap3216c_read_reg The function is used to read the data of the specified register of AP3216C and to read the data of a register.
 
 */
static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg)
{
	u8 data = 0;

	ap3216c_read_regs(dev, reg, &data, 1);
	return data;

#if 0
	struct i2c_client *client = (struct i2c_client *)dev->private_data;
	return i2c_smbus_read_byte_data(client, reg);
#endif
}

/*
 * @description	: Write the specified value to the specified register of ap3216c and write a register
 * @param - dev:  ap3216c equipment
 * @param - reg:  Register to write
 * @param - data: Value to write
 * @return   :    nothing
 ap3216c_write_reg The function is used to write data to the specified register of AP3216C and to write data to a register.
 */
static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data)
{
	u8 buf = 0;
	buf = data;
	ap3216c_write_regs(dev, reg, &buf, 1);
}

/*
 * @description	: Read the data of AP3216C and read the original data, including ALS,PS and IR. Note!
 *				: If ALS and IR + PS are turned on at the same time, the time interval between two data reads should be greater than 112.5ms
 * @param - ir	: ir data
 * @param - ps 	: ps data
 * @param - ps 	: als data 
 * @return 		: None.

 Read the original data values of sensors such as PS, ALS and IR of AP3216C.
 */
void ap3216c_readdata(struct ap3216c_dev *dev)
{
	unsigned char i =0;
    unsigned char buf[6];
	
	/* Cycle through all sensor data */
    for(i = 0; i < 6; i++)	
    {
        buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i);	
    }

    if(buf[0] & 0X80) 	/* IR_OF If the bit is 1, the data is invalid */
		dev->ir = 0;					
	else 				/* Read IR sensor data   		*/
		dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03); 			
	
	dev->als = ((unsigned short)buf[3] << 8) | buf[2];	/* Read data from ALS sensor 			 */  
	
    if(buf[4] & 0x40)	/* IR_OF If the bit is 1, the data is invalid 			*/
		dev->ps = 0;    													
	else 				/* Read PS sensor data    */
		dev->ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F); 
}

/*
 * @description		: open device
 * @param - inode 	: inode passed to driver
 * @param - filp 	: The device file has a file structure called private_ Member variable of data
 * 					  Generally, private is used when open ing_ Data points to the device structure.
 * @return 			: 0 success; Other failures
 */
static int ap3216c_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &ap3216cdev;

	/* Initialize AP3216C */
	ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x04);		/* Reset AP3216C 			*/
	mdelay(50);														/* AP3216C Reset for at least 10ms 	*/
	ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0X03);		/* Turn on ALS, PS+IR 		*/
	return 0;
}

/*
 * @description		: Read data from device 
 * @param - filp 	: Device file to open (file descriptor)
 * @param - buf 	: Data buffer returned to user space
 * @param - cnt 	: Length of data to read
 * @param - offt 	: Offset relative to the first address of the file
 * @return 			: The number of bytes read. If it is negative, it indicates that the read failed
 */
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
	short data[3];
	long err = 0;

	struct ap3216c_dev *dev = (struct ap3216c_dev *)filp->private_data;
	
	ap3216c_readdata(dev);

	data[0] = dev->ir;
	data[1] = dev->als;
	data[2] = dev->ps;
	err = copy_to_user(buf, data, sizeof(data));
	return 0;
}

/*
 * @description		: Turn off / release the device
 * @param - filp 	: Device file to close (file descriptor)
 * @return 			: 0 success; Other failures
 */
static int ap3216c_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/* AP3216C Operation function */
static const struct file_operations ap3216c_ops = {
	.owner = THIS_MODULE,
	.open = ap3216c_open,
	.read = ap3216c_read,
	.release = ap3216c_release,
};

 /*
  * @description     : i2c Driven probe function, when driven with
  *                    This function will be executed after the device matches
  * @param - client  : i2c equipment
  * @param - id      : i2c Device ID
  * @return          : 0,success; Other negative values, failed

ap3216c_probe Function. After the I2C device and driver are successfully matched, this function will be executed, just like the platform driver framework. This function is preceded by
 Is the standard character device registration code. The first parameter client of this function will be passed to ap3216cdev's private_data member change
 Quantity.
  */
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	/* 1,Build equipment number */
	if (ap3216cdev.major) {
		ap3216cdev.devid = MKDEV(ap3216cdev.major, 0);
		register_chrdev_region(ap3216cdev.devid, AP3216C_CNT, AP3216C_NAME);
	} else {
		alloc_chrdev_region(&ap3216cdev.devid, 0, AP3216C_CNT, AP3216C_NAME);
		ap3216cdev.major = MAJOR(ap3216cdev.devid);
	}

	/* 2,Register device */
	cdev_init(&ap3216cdev.cdev, &ap3216c_ops);
	cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT);

	/* 3,Create class */
	ap3216cdev.class = class_create(THIS_MODULE, AP3216C_NAME);
	if (IS_ERR(ap3216cdev.class)) {
		return PTR_ERR(ap3216cdev.class);
	}

	/* 4,Create device */
	ap3216cdev.device = device_create(ap3216cdev.class, NULL, ap3216cdev.devid, NULL, AP3216C_NAME);
	if (IS_ERR(ap3216cdev.device)) {
		return PTR_ERR(ap3216cdev.device);
	}

	ap3216cdev.private_data = client;

	return 0;
}

/*
 * @description     : i2c The remove function of the driver. This function will be executed when the i2c driver is removed
 * @param - client 	: i2c equipment
 * @return          : 0,success; Other negative values, failed
 */
static int ap3216c_remove(struct i2c_client *client)
{
	/* Delete device */
	cdev_del(&ap3216cdev.cdev);
	unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);

	/* Unregister classes and devices */
	device_destroy(ap3216cdev.class, ap3216cdev.devid);
	class_destroy(ap3216cdev.class);
	return 0;
}

/* Traditional matching method ID list
ap3216c_id Match table, i2c_device_id type. It is used for traditional device and driver matching, that is, when the device tree is not used.
 */
static const struct i2c_device_id ap3216c_id[] = {
	{"alientek,ap3216c", 0},  
	{}
};

/* Device tree matching list
ap3216c_of_match Matching table, of_device_id type, used for device tree device and driver matching. Only one compatible genus is written here
 The value is "alientek,ap3216c".
 */
static const struct of_device_id ap3216c_of_match[] = {
	{ .compatible = "alientek,ap3216c" },
	{ /* Sentinel */ }
};

/* i2c Drive structure */	
static struct i2c_driver ap3216c_driver = {
	.probe = ap3216c_probe,
	.remove = ap3216c_remove,
	.driver = {
			.owner = THIS_MODULE,
		   	.name = "ap3216c",
		   	.of_match_table = ap3216c_of_match, 
		   },
	.id_table = ap3216c_id,
};
		   
/*
 * @description	: Drive entry function
 * @param 		: nothing
 * @return 		: nothing
 */
static int __init ap3216c_init(void)
{
	int ret = 0;

	ret = i2c_add_driver(&ap3216c_driver);
	return ret;
}

/*
 * @description	: Drive exit function
 * @param 		: nothing
 * @return 		: nothing
 */
static void __exit ap3216c_exit(void)
{
	i2c_del_driver(&ap3216c_driver);
}

/* module_i2c_driver(ap3216c_driver) */

module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");

Novice introduction, the above are summarized from the punctual atomic tutorial.
Pure personal learning notes, easy to find at any time. Thanks for reading

Tags: Linux Linux Driver

Posted on Wed, 22 Sep 2021 13:34:18 -0400 by Ang3l0fDeath