I2C subsystem framework, i2c Subsystem
1 I2C subsystem framework
The Linux I2C subsystem consists of the I2C core layer, I2C bus driver, and I2C device driver.
(1) I2C core layer
The I2C core provides the registration and cancellation methods for I2C bus drivers and device drivers, and the I2C communication method (that is, algorithm) code irrelevant to the specific adapter on the upper layer, as well as the Code for detecting the device and detecting the device address on the upper layer. The core layer code is implemented in drivers/i2c/i2c-core.c.
(2) I2C bus driver
The I2C bus driver is an implementation of the I2C hardware adapter, which can be controlled by the CPU. The I2C bus driver mainly includes the i2c_adapter data structure of the I2C adapter, the Algorithm Data Structure i2c_algorithm of the I2C adapter, and the function that controls the communication signal generated by the I2C adapter.
With the code driven by I2C bus, we can control the I2C adapter to generate the start bit, stop bit, read/write cycle, read/write and generate ACK in the form of a device.
Linux has established the busses directory in drivers/i2c/to store various implemented I2C bus drivers, including samsung S3C series chip I2C bus driver for i2c-s3c2410.c.
(3) I2C Device Driver
The I2C device driver is the implementation of I2C hardware devices. devices are generally attached to the CPU-controlled I2C controller and exchange data with the CPU through the I2C adapter. The I2C Device Driver mainly includes the data structure i2c_driver and i2c_client. We need to implement the member functions based on the specific device.
[Tip] i2c-dev.c to achieve the I2C adapter device file function, each I2C adapter is assigned a device. When you access a device through an adapter, both the master and secondary device numbers are 89 and the secondary device numbers are 0 ~ 255. I2c-dev.c is not designed for specific devices, but provides general interfaces such as read (), write () and ioctl, the application layer can use these interfaces to access the storage space or registers of I2C devices attached to the adapter and control how the I2C devices work.
2 I2C subsystem Data Structure
(1) i2c_adapter Structure
The I2C subsystem uses the struct i2c_adapter to describe a physical Adapter. The communication method of the adapter is described by a member of the struct i2c_algorithm type. It is defined in include/linux/i2c. h.
1 struct i2c_adapter {2 struct module * owner; 3 unsigned int id; // adapter ID, which is not commonly used in the Adapter Driver 4 unsigned int class; // adapter class type 5 const struct i2c_algorithm * algo; // pointer to communication method data 6 void * algo_data; 7 8/* data fields that are valid for all devices */9 u8 level;/* nesting level for lockdep */10 struct mutex bus_lock; 11 12 int timeout; // transmission timeout 13 int retries; // Number of retries 14 struct device dev; // embedded device structure 15 16 int nr; // Bus No. (also adapter No ), the corresponding device node/dev/i2c-x (x = 0, 1, 2 ...) to access 17 char name [48]; // adapter name, which can be through/sys/bus/i2c/devices/i2c-x/name (x =, 2 ...) to access 18 struct completion dev_released; 19 };
(2) i2c_algorithm Structure
I2c_algorithm, a member used to describe the communication method in struct i2c_adapter, is defined as follows. The implementation of this member and its kernel method is the core task of developing the I2C bus driver. This struct is defined in include/linux/i2c. h.
1 struct i2c_algorithm {2/* points to a specific I2C transmission function pointer. The corresponding transmission is generally initiated by directly operating the adapter hardware. The input parameters of the three functions are the adapter adap that uses the transmission method, the message msgs to be transmitted, and the number of messages num. */4 int (* master_xfer) (struct i2c_adapter * adap, struct i2c_msg * msgs, 5 int num); 6/* pointer to the specific SMBus transport function. Most of the SMBus protocols are based on I2C bus specifications and are extended based on some differences in the access 7 time series. If this pointer is set to NULL, SMBus-based communication will be simulated through I2C transmission */8 int (* smbus_xfer) (struct i2c_adapter * adap, 2010addr, 9 unsigned short flags, char read_write, 10 u8 command, int size, union i2c_smbus_data * data); 11 12/* pointer to the function that returns the functions supported by the adapter, which are defined in incude/linux/i2c. h is represented by a macro starting with I2C_FUNC 13. Commonly Used macros include I2C_FUNC_I2C, I2C_FUNC_SMBUS_EMUL, I2C_FUNC_PROTOCOL_MANGLING */14 u32 (* functionality) (struct i2c_adapter };
(3) i2c_msg Structure
The i2c_msg structure is used to describe a message used to transmit I2C. This struct is defined in include/linux/i2c. h.
1 struct i2c_msg {2 _ addr; // slave address 3 _ flags; // indicates the message-specific flag. When the I2C_M_RD bit is set, indicates that the message direction is read from the host. If 4 // is not set to this bit (0), the message direction is written from the host to the slave. I2C_M_RD can be processed by all adapters. For messages such as I2C_M_NOSTART, the adapter must support the I2C_FUNC_PROTOCOL_MANGLING function. 6 # define I2C_M_TEN 0x0010/* this is a ten bit chip address */7 # define I2C_M_RD 0x0001/* read data, from slave to master */8 # define I2C_M_NOSTART 0x4000/* if I2C_FUNC_NOSTART */9 # define slave 0x2000/* if slave */10 # define I2C_M_IGNORE_NAK 0x1000/ * if I2C_FUNC_PROTOCOL_MANGLING */11 # define I2C_M_NO_RD_ACK 0x0800/* if exist */12 # define I2C_M_RECV_LEN 0x0400/* length will be first initialized ed byte */13 _ hot len; // message data length, in byte 14 _ u8 * buf; // point to message data storage buffer 15 };
An i2c_msg object represents an underlying I2C transmission unit. In the driver, the i2c_msg object is processed through the i2c_transfer () function, while the i2c_transfer () method calls the master_xfer () method of the adapter communication method to transmit message data.
(4) i2c_driver Structure
The i2c_driver structure is defined in include/linux/i2c. h. It corresponds to a set of driving methods. Its main member functions include probe (), remove (), suspend (), and resume (). In addition, the id_table in the form of struct i2c_device_id is the ID table of the I2C device supported by the driver.
1 struct i2c_driver {2 unsigned int class; 3 4/* is the function pointer that is attached to and out of the i2c_adapter, respectively, the driver registration function traverses all the devices in the i2c_adapter_class of the adapter device Class 5 and calls the attach_adapter method of the driver for attachment. Correspondingly, when the i2c_adapter is added, the adapter registration function traverses all the drivers on the i2c_bus_type 6 bus. If the driver defines the attach_adapter method, he will also get the call */7 int (* attach_adapter) (struct i2c_adapter *) _ deprecated; 8 int (* detach_adapter) (struct i2c_adapter *) _ deprecated; 9 10/* in the device driver, the device on the bus i2c_bus_type is called after matching with the device driver. */11 int (* probe) (struct i2c_client *, const struct i2c_device_id *); 12 int (* remove) (struct i2c_client *); 13 14/* driver model interfaces that don't relate to enumeration */15 void (* shutdown) (struct i2c_client *); 16 int (* suspend) (struct i2c_client *, pm_message_t mesg); 17 int (* resume) (struct i2c_client *); 18 19 void (* alert) (struct i2c_client *, unsigned int data); 20 21 int (* command) (struct I2c_client * client, unsigned int cmd, void * arg); 22 23/* embedded driver structure. When registering an i2c_driver object, the i2c_driver-> driver bus type is specified as i2c_bus_type */24 struct device_driver driver; 25/* stores the list of devices supported by the driver. The i2c_device id structure is defined in include/linux/mod_devicetable.h26. It includes a char name [I2C_NAME_SIZE] and a member of kernel_ulong_t driver_data. 27 name is used to match the device and driver. The match () method of i2c_bus_type traverses each item in the driver id_table, compares the device name and the name member through 28, and finds the driver that matches the device. */29 const struct i2c_device_id * id_table; 30 31 int (* detect) (struct i2c_client *, struct i2c_board_info *); 32 const unsigned short * address_list; 33 struct list_head client; 34 };
(5) i2c_client Structure
The i2c_client structure is defined in inlcude/linux/i2c. h. The i2c_client corresponds to a real physical device. Each I2C device requires an i2c_client description. I2c_client information is usually filled with i2c_board_info or Device Tree in the BSP board-level file.
1 struct i2c_client {2 unsigned short flags; // the two main identifiers are I2C_CLIENT_TEN, indicating that the device uses a 10-bit address; 3 // I2C_CLIENT_PEC indicates that the device uses the SMBus packet error check 4 unsigned short addr; // device address, in the 7-bit address format, the address stores the Member's low 7-bit 5 char name [I2C_NAME_SIZE]; // The device name 6 struct i2c_adapter * adapter; // The attached adapter 7 struct i2c_driver * driver; // drive 8 struct device dev bound to the device; // The embedded device structure. 9 int irq; 10 struct list_head detected; 11 };
(1) Fill i2c_board_info In the BSP board-level file:
The I2C device ID is "ad7142_joystick", the address is 0x2C, And the interrupt number is IRQ_PF5.
1 static struct i2c_board_info __initdata xxx_i2c_board_info[] = {2 {3 I2C_BOARD_INFO("ad7142_joystick", 0x2C);4 .irq = IRQ_PF5.5 },6 ...7 }
(2) Device Tree filling:
The I2C device ID is "invalid sense, mpu6050", the address is 0x68, and the interrupt number is 3.
i2c@138B0000 { samsung,i2c-sda-delay = <100>; samsung,i2c-max-bus-freq = <20000>; pinctrl-0 = <&i2c5_bus>; pinctrl-names = "default"; status = "okay"; mpu6050-3-axis@68 { compatible = "invensense,mpu6050"; reg = <0x68>; interrupt-parent = <&gpx3>; interrupts = <3 2>; };};
In the i2c_device_match () function of the i2c_bus_type driver on the I2C bus, the i2c_match_id () function is called to match the IDS defined in the board-level file and the IDS supported by the i2c_driver.
3 I2C subsystem Interface
(1) i2c_add_adapter ()
I2c_add_adapter () registers an i2c_adapter object with the system and does not need to specify its nr Member. The bus number is automatically assigned to nr in i2c_add_adapter.
Function prototype |
Int i2c_add_adapter (struct i2c_adapter * adapter ); |
Function Parameters |
Adaper: i2c_adapter structure pointer |
(2) i2c_del_adaper ()
I2c_del_adapter () cancels an i2c_adapter object from the system.
Function prototype |
Int i2c_del_adapter (struct i2c_adapter * adapter ); |
Function Parameters |
Adaper: i2c_adapter structure pointer |
(3) i2c_add_numbered_adapter ()
I2c_add_numbered_adapter () registers an i2c_adapter structure with the system. This function must statically specify a bus number to the nr member.
Function prototype |
Int i2c_add_numbered_adapter (struct i2c_adapter * adapter ); |
Function Parameters |
Adaper: i2c_adapter structure pointer |
(4) i2c_add_driver ()
I2c_add_driver () adds an i2c_driver object to the system.
Function prototype |
Int i2c_add_driver (struct i2c_driver * driver ); |
Function Parameters |
Driver: i2c_driver structure pointer |
(5) i2c_del_driver ()
I2c_del_driver () cancels an i2c_driver object from the system.
Function prototype |
Int i2c_del_driver (struct i2c_driver * driver ); |
Function Parameters |
Driver: i2c_driver structure pointer |
(6) i2c_master_send ()
The i2c_master_send () function is used by the host to send data to the i2c_client device object. When using them to send and receive data, you must first set the device address.
Function prototype |
Int i2c_master_send (const struct i2c_client * client, const char * buf, int count ); |
Function Parameters |
Client: i2c_client structure pointer |
Buf: buffer for storing sent data |
Count: size of data sent |
(7) i2c_master_recv ()
The i2c_master_recv () function is used by the host to obtain data from the slave. When sending and receiving data, you must first set a device node.
Function prototype |
Int i2c_master_recv (const struct i2c_client * client, const char * buf, int count ); |
Function Parameters |
Client: i2c_client structure pointer |
Buf: buffer for storing received data |
Count: the size of the received data |
(8) i2c_transfer ()
The i2c_transfer () function is used to execute a single or combined MSG message.
Function prototype |
Int i2c_transfer (struct i2c_adapter * adap, struct i2c_msg * msgs, int num ); |
Function Parameters |
Adapter: Structure pointer of I2C adapter |
Msgs: i2c_msg structure, which stores sent and received data |
Num: Number of msg |
[Tip] i2c_transfer () can transmit multiple i2c_msg at a time (considering that the reading and writing waveforms of many peripherals are complicated ). For peripherals with relatively simple time series, the i2c_master_send () function and the i2c_master_recv () function call the i2c_transfer () function to complete one write message and one read message respectively.