[Beijing Xunwei] i.MX6ULL Terminator Linux I2C driver experiment I2C device driver writing process

The I2C bus driver generally does not need to be written by the user, while the I2C device driver needs to be written by the user. The driver of each device is different, but the writing process is the same. Let's analyze it in detail below.

1 Device information description

1. Do not use the device tree file.
When starting to write the I2C device driver, first add device information. Let's first look at how to add I2C device information to the platform file when using the platform file without using the device tree.
A specific I2C device is described in the platform file through the i2c_board_info structure. The i2c_board_info structure is as follows:

295 struct i2c_board_info {
    
     
296        char type[I2C_NAME_SIZE]; /* I2C 设备名字 */ 
297        unsigned short flags; /* 标志 */ 
298        unsigned short addr; /* I2C 器件地址 */ 
299      void *platform_data; 
300        struct dev_archdata *archdata; 
301        struct device_node *of_node; 
302        struct fwnode_handle *fwnode; 
303        int irq; 
304 }; 

The two member variables type and addr must be set, one is the name of the I2C device, and the other is the device address of the I2C device. Open the arch/arm/mach-imx/mach-mx27_3ds.c file, the I2C device information about OV2640 in this file is described as follows:

392 static struct i2c_board_info mx27_3ds_i2c_camera = {
    
     
393 		I2C_BOARD_INFO("ov2640", 0x30), 
394 }; 

I2C_BOARD_INFO is used to complete the initialization of mx27_3ds_i2c_camera. I2C_BOARD_INFO is a macro defined as follows:

316 #define I2C_BOARD_INFO(dev_type, dev_addr) \ 
317 				.type = dev_type, .addr = (dev_addr) 

It can be seen that the I2C_BOARD_INFO macro is actually to set the two member variables of i2c_board_info type and addr. You can find many similar I2C devices in the platform files. There are not only I2C devices in the platform files, but also many other different types of devices. The more devices there are, the more confusing the platform files appear, so a device tree file is generated.
2. Using the device tree file
When using the device tree file, you only need to add the corresponding I2C device node in the device tree file. For example, the official NXP EVK development board is connected to the I2C1 magnetometer chip mag3110, so it must Create a mag3110 child node under the i2c1 node, and then describe the related information of the mag3110 chip in this child node. Open the device tree file imx6ull-14x14-evk.dts, and find the following:

1 &i2c1 {
    
    
2      clock-frequency = <100000>; 
3      pinctrl-names = "default"; 
4      pinctrl-0 = <&pinctrl_i2c1>; 
5      status = "okay"; 
6 
7      mag3110@0e {
    
     
8          compatible = "fsl,mag3110"; 
9          reg = <0x0e>; 
10             position = <2>; 
11         }; 
...... 
20 };

Lines 7~11, add the mag3110 sub-node to i2c1, line 7 "mag3110@0e" is the name of the sub-node, and the "0e" after "@" is the I2C device address of mag3110. Line 8 sets the compatible attribute value to "fsl,mag3110". The reg attribute in line 9 also sets the device address of mag3110, so the value is 0x0e. The creation of the I2C device node focuses on the setting of the compatible attribute and the reg attribute, one for matching the driver and the other for setting the device address.

2 Data receiving and processing of I2C devices

In the I2C device driver, the creation, initialization and registration of the i2c_driver structure must be completed first. When the device and the driver match successfully, the probe function will be executed. The probe function is a set of processes for executing the character device driver.
Generally, the I2C device
needs to be initialized in the probe function. To initialize the I2C device, it must be able to read and write the I2C device registers. The i2c_transfer function is used here. The i2c_transfer function will eventually call the master_xfer function in i2c_algorithm in the I2C adapter, which is the function i2c_imx_xfer for I.MX6U. The prototype of i2c_transfer function is as follows:

int i2c_transfer(struct i2c_adapter *adap, 
struct i2c_msg *msgs, 
int num) 

The meanings of function parameters and return values ​​are as follows:
adap: the I2C adapter used, i2c_client will save its corresponding i2c_adapter.
msgs: One or more messages to be sent by I2C.
num: The number of messages, that is, the number of msgs.
Return value: negative value, failure, other non-negative values, the number of msgs sent.
Let's focus on the msgs parameter, which is a pointer parameter of type i2c_msg. I2C data transmission and reception is simply the transmission of messages. The Linux kernel uses the i2c_msg structure to describe a message. The i2c_msg structure is defined in the include/uapi/linux/i2c.h file, and the structure content is as follows:

68 struct i2c_msg {
    
     
69         __u16 addr; /* 从机地址 */ 
70         __u16 flags; /* 标志 */ 
71         #define I2C_M_TEN 0x0010 
72         #define I2C_M_RD 0x0001 
73         #define I2C_M_STOP 0x8000 
74         #define I2C_M_NOSTART 0x4000
75      #define I2C_M_REV_DIR_ADDR 0x2000 
76         #define I2C_M_IGNORE_NAK 0x1000 
77         #define I2C_M_NO_RD_ACK 0x0800 
78         #define I2C_M_RECV_LEN 0x0400 
79         __u16 len; /* 消息(本 msg)长度 */ 
80         __u8 *buf; /* 消息数据 */ 
81 };

Before using the i2c_transfer function to send data, you must first build i2c_msg. The sample code for using i2c_transfer to send and receive I2C data is as follows:

1 /* 设备结构体 */ 
2 struct xxx_dev {
    
     
3      ...... 
4      void *private_data; /* 私有数据,一般会设置为 i2c_client */ 
5 }; 
6 
7 /* 
8 * @description : 读取 I2C 设备多个寄存器数据 
9 * @param – dev : I2C 设备 
10 * @param – reg : 要读取的寄存器首地址 
11 * @param – val : 读取到的数据 
12 * @param – len : 要读取的数据长度 
13 * @return : 操作结果 
14 */ 
15 static int xxx_read_regs(struct xxx_dev *dev, u8 reg, void *val, int len) 
16 {
    
     
17         int ret; 
18         struct i2c_msg msg[2]; 
19         struct i2c_client *client = (struct i2c_client *) dev->private_data; 
20 
21         /* msg[0],第一条写消息,发送要读取的寄存器首地址 */ 
22         msg[0].addr = client->addr; /* I2C 器件地址 */ 
23         msg[0].flags = 0; /* 标记为发送数据 */ 
24         msg[0].buf = &reg; /* 读取的首地址 */ 
25         msg[0].len = 1; /* reg 长度 */ 
26 
27         /* msg[1],第二条读消息,读取寄存器数据 */ 
28         msg[1].addr = client->addr; /* I2C 器件地址 */ 
29         msg[1].flags = I2C_M_RD; /* 标记为读取数据 */ 
30         msg[1].buf = val; /* 读取数据缓冲区 */ 
31         msg[1].len = len; /* 要读取的数据长度 */
32 
33         ret = i2c_transfer(client->adapter, msg, 2); 
34         if(ret == 2) {
    
     
35             ret = 0; 
36         } else {
    
     
37             ret = -EREMOTEIO; 
38         } 
39         return ret; 
40 } 
41 
42 /* 
43 * @description : 向 I2C 设备多个寄存器写入数据 
44 * @param – dev : 要写入的设备结构体 
45 * @param – reg : 要写入的寄存器首地址 
46 * @param – val : 要写入的数据缓冲区 
47 * @param – len : 要写入的数据长度 
48 * @return : 操作结果 
49 */ 
50 static s32 xxx_write_regs(struct xxx_dev *dev, u8 reg, u8 *buf, u8 len) 
51 {
    
     
52         u8 b[256]; 
53         struct i2c_msg msg; 
54         struct i2c_client *client = (struct i2c_client *) dev->private_data; 
55 
56         b[0] = reg; /* 寄存器首地址 */ 
57         memcpy(&b[1],buf,len); /* 将要发送的数据拷贝到数组 b 里面 */ 
58 
59         msg.addr = client->addr; /* I2C 器件地址 */ 
60         msg.flags = 0; /* 标记为写数据 */ 
61 
62         msg.buf = b; /* 要发送的数据缓冲区 */ 
63         msg.len = len + 1; /* 要发送的数据长度 */ 
64 
65         return i2c_transfer(client->adapter, &msg, 1); 
66 }

Lines 2~5, device structure, add a pointer member variable private_data that executes void in the device structure, this member variable is used to save the private data of the device. In the I2C device driver, we generally point it to the i2c_client corresponding to the I2C device.
In lines 15-40, the xxx_read_regs function is used to read data from multiple registers of the I2C device. Line 18 defines an i2c_msg array with 2 array elements. When I2C reads data, it must first send the register address to be read, and then read the data, so two i2c_msgs need to be prepared. One is used to send the register address and one is used to read the register value. For msg[0], set flags to 0, which means write data. The addr of msg[0] is the device address of the I2C device, and the buf member variable of msg[0] is the address of the register to be read. For msg[1], set flags to I2C_M_RD, which means read 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 the i2c_transfer function to complete the I2C data read operation.
In lines 50~66, the xxx_write_regs function is used to write data to multiple registers of the I2C device. The I2C write operation is a bit simpler than the read operation, so one i2c_msg is enough. Array b is used to store the first address of the register and the data to be sent. Line 59 sets the addr of msg to the I2C device address. Set the flags of msg to 0 in line 60, that is, write data. Line 62 sets the data to be sent, which is the array b. Line 63 sets the len of msg to len+1, because one byte of register address is added. Finally, the write operation to the I2C device is completed through the i2c_transfer function. There are also two API functions for I2C data sending and receiving operations, both of which will eventually call i2c_transfer. First look at the I2C data sending function i2c_master_send, the function prototype is as follows:

int i2c_master_send(const struct i2c_client *client, 
const char *buf, 
int count) 

The meanings of function parameters and return values ​​are as follows:
client: i2c_client corresponding to the I2C device.
buf: The data to be sent.
count: The number of data bytes to be sent, which must be less than 64KB, because the len member variable of i2c_msg is a u16 (unsigned 16-bit) type of data.
Return value: Negative value, failure, other non-negative values, the number of bytes sent.
The I2C data receiving function is i2c_master_recv, and the function prototype is as follows:

int i2c_master_recv(const struct i2c_client *client, 
char *buf, 
int count) 

The meanings of function parameters and return values ​​are as follows:
client: i2c_client corresponding to the I2C device.
buf: The data to be received.
count: the number of data bytes to be received, which should be less than 64KB, thinking that the len member variable of i2c_msg is a u16 (unsigned 16-bit) type of data.
Return value: Negative value, failure, other non-negative values, the number of bytes sent.
The writing process of the I2C device driver under Linux is explained here. The focus is on the construction of
i2c_msg and the call of the i2c_transfer function. Next, we will write the Linux driver for AP3216C, the I2C device.

Insert picture description here

Guess you like

Origin blog.csdn.net/BeiJingXunWei/article/details/112506159