Timeline
Timeline
2025-12-28
init
This article introduces the basics of the Linux I2C bus, discussing in detail its master-slave architecture, communication protocol, addressing mechanism, and multi-master support. Combining with the RK3568 platform, it summarizes the implementation differences between hardware I2C and software I2C, and explains the key role of pull-up resistors in ensuring the bus idle level and implementing the wired-AND function.
Linux Driver Notes
| Table of Contents | Links |
|---|---|
| 1. Linux Driver Framework | |
| 2. Linux Driver Loading Logic | |
| 3. Character Device Basics | |
| 4. Concurrency and Competition | |
| 5. Advanced Character Device Progression | |
| 6. Interrupts | |
| 7. Platform Bus | |
| 8. Device Tree | |
| 9. Device Model | |
| 10. Hot Plug | |
| 11. pinctrl Subsystem | |
| 12. gpio Subsystem | |
| 13. Input Subsystem | |
| 14. Single Bus | |
| 15. I2C | |
| 16. SPI | |
| 17. UART | |
| 18. PWM | |
| 19. RTC | |
| 20. Watchdog | |
| 21. CAN | |
| 22. Network Device | |
| 23. ADC | |
| 24. IIO | |
| 25. USB | |
| 26. LCD |
Introduction to I2C
The famous Dutch electronics company Philips invented an integrated circuit interconnection communication protocol called I2C (Inter-Integrated Circuit).

Inthe idle state,SDA and SCL are generally pulled high by pull-up resistors, maintaining a high level state, when data transmission is needed, the required signals for the I2C bus are generated through the high and low levels of SCL and SDA to perform data transfer.
Features
- Bus Topology
The I2C bus adopts a master-slave architecture, consisting of one master device and one or more slave devices. The master device is responsible for initiating data transmission, while the slave devices respond to the master’s requests.
- Physical Layer Interface
The I2C bus uses two lines for communication:**SCL(Serial Clock Line)**Clock line (SCL), which provides the clock signal from the master device.
SDA(Serial Data LineData line (SDA), used for bidirectional data transmission.
These two lines typically require pull-up resistors to maintain the signal at a high level.
- Communication Protocol
I2C usessynchronous serial communicationmethod, where the master initiates communication and provides the clock. The master first sends a “start” signal, then sends the slave device address and data transfer direction (read or write). After receiving its own address, the slave sends an acknowledgment signal, indicating it is ready to receive or send data. Then the master and slave can begin data transmission. At the end of communication, the master sends a “stop” signal.
Clock Frequency
The I2C bus supports multiple communication rates, common ones include:Standard mode: 100 kbps
Fast mode: 400 kbps
High-speed mode: 3.4 Mbps
Addressing Mechanism
I2C uses a 7-bit address space, capable of addressing up to 127 slave devices. The first 7 bits of the address space are used to specify the slave device, and the last bit is used to indicate the read/write direction.
Each I2C peripheral corresponds to a unique address (this address can be obtained from the I2C peripheral device’s datasheet). Communication between the master and slave is determined by this address to identify which slave the master intends to communicate with.Multi-Master Support
The I2C bus supports multiple master devices sharing the same bus,avoiding conflicts through an arbitration mechanism. When multiple master devices attempt to occupy the bus simultaneously,the master device with the highest priority will gain control of the bus。Other Features:
- The maximum bus capacitance is limited to 400pF。
- Data is transmitted in units of bytes。
- There are two implementation methods: hardware I2C and software I2C.
I2C on RK3568

- Supports 6 I2C interfaces, namely I2C0, I2C1, I2C2, I2C3, I2C4, I2C5
- Supports 7-bit and 10-bit address modes
- Software programmable clock frequency
- Data transfer rate on the I2C bus can reach
- Standard mode up to 100Kbit/s
- Fast mode up to 400Kbit/s
- Fast mode plus up to 1Mbit/s
Hardware I2C and software I2C
The 6 I2C interfaces here refer toHardware I2C,There is a dedicated hardware I2C circuit on the SOC, the introduction to hardware I2C is as follows
Hardware I2C
Hardware I2C,There is a dedicated hardware I2C circuit on the SOC
- Implementation method: The I2C bus protocol is implemented through a dedicated hardware I2C interface circuit.
- Advantages: Low CPU usage, the I2C bus is automatically completed by hardware circuits. High transmission rate, up to 400kbit/s or 3.4Mbit/s. More reliable and stable, less susceptible to external interference.
- Disadvantages: Requires dedicated hardware I2C interface circuit support, relatively high cost. The interface is fixed, not as flexible as software I2C.
- Scope of Application: Suitable for high-speed, large data transmission scenarios, such as connecting peripherals like LCD, EEPROM, etc.
Software I2C
refers tosimulating the SCL and SDA signal lines through GPIO pins, when hardware I2C is insufficient, software I2C can be simulated through GPIO. The introduction to software I2C is as follows:
- Implementation Method: Simulates the I2C bus protocol through software, using general-purpose I/O pins to simulate the SCL and SDA signal lines.
- Advantages: High flexibility, can implement I2C interface on any I/O pin. Low cost, no additional hardware support required.
- Disadvantages: High CPU usage because I2C timing needs to be simulated in software. The transmission rate is low, limited by CPU performance, typically around 100kbit/s.
- Scope of Application: Suitable for low-speed, small data transmission scenarios.
I2C Pull-up Resistors

In I2C, a pull-up resistor is required on both the SDA data line and the SCL clock line.
Function of Pull-up Resistors
- Ensure the bus remains at a high level when idle.
The I2C bus uses open-drain/open-collector output. When no device is driving the bus, the bus is in a high-impedance state.. Without pull-up resistors, the bus level is undefined and susceptible to noise interference. Pull-up resistors canensure a stable high level when the bus is idle.。
- Implement the wired-AND function.
The I2C bus allows multiple devices to be connected to the same bus.。When one device pulls the bus low, the outputs of other devices are also pulled low. This is the wired-AND function, enabling bus arbitration.. Without pull-up resistors, the level is undefined when the bus is idle, and when one device pulls the bus low, other devices cannot detect the level change, making the wired-AND function impossible.
The pull-up resistor here cannot be arbitrarily chosen; it must considerthe bus capacitance. Various parasitic capacitances exist on the I2C bus, which can be equivalent to an RC charging circuit, as shown in the figure below

When the bus transitions from low to high level, the pull-up resistor supplies power to the bus, charging the bus capacitance.
If the pull-up resistor value is too large, the charging time becomes too long, causing a slow rising edge that may affect communication;
If the pull-up resistor value is too small, a low level cannot be generated
Therefore, the I2C specification requires that the bus capacitance not exceed 400pF. It is generally recommended to choose between 1kΩ and 10kΩ to ensure both rising edge speed and reliable pulling down of the bus level
I2C pull-up resistor value calculation
Minimum value
Formula:
- is typically a common supply voltage such as 5V or 3.3V; on the iTOP-RK3568 development board, it is 3.3V
- represents the maximum output voltage of the device at low level. The specific values are shown in the table below. Since VDD is 3.3V, the maximum value of VOL is 0.4

- The maximum sink current of the device at low level, with specific values shown in the table below, throughvalue of 0.4, we can obtainThe value in standard mode and fast mode is 3 mA.

Calculate:
- Determine、andvalues: In RK3568,, take, the corresponding。
- Substitute into the formula: to calculate the minimum pull-up resistor value: Usually, standard resistor values can be taken as910 Ωor1 kΩ(if slightly below the theoretical minimum is allowed, it needs to be combined with actual bus capacitance and speed requirements).
Maximum value
Formula:
- : Maximum value of pull-up resistor (unit: Ω)
- : Bus capacitance (unit: F), including the sum of PCB trace capacitance, pin capacitance, and device input capacitance
- : High-level rise time (unit: s), typically measured from 0.3VDD to 0.7VDD, details can be obtained from the datasheet

From the above figure, it can be obtained
- In standard modethe value is ≤ 1000ns.
- In fast modethe value is ≤ 300ns.
- In ultra-fast modethe value is ≤ 120ns
Calculation:
I²C operates in standard mode, the pull-up voltage is, the pin capacitance is, the connection capacitance is, the high-level rise time. Calculate the maximum value of the pull-up resistor.
- Calculate the bus capacitance:
- Substitute into the formula:
- Substitute values for calculation:
Therefore, the maximum value of the pull-up resistor is approximately29.51 kΩ。
Specific selection
Generally, the faster the I2C bus speed, the smaller the required pull-up resistor value. The specific selection is as follows:
100kbps: Typically choose a 10k pull-up resistor
400kbps: Typically choose a 4.7k pull-up resistor
1Mbps: Typically choose a 2.2k pull-up resistor
Of course, the above selection may not be correct and needs to be adjusted based on actual test results. In practice, you can first choose a pull-up resistor value based on experience for trial use, without being overly fixated on the calculation formula.
I2C communication timing
Start signal and stop signal
All interactions areinitiated by the START (S) signal, andterminated by the STOP (P) signal. The specific communication timing diagrams for the start and stop signals are as follows:

- Start Signal (START): Generated by the bus controller (i.e., the master), it is defined asa transition of the SDA line from high to low while the SCL line remains high。
- Stop Signal (STOP): Generated by the bus controller, it is defined asa transition of the SDA line from low to high while the SCL line remains high。
After the START signal, the bus is considered busy until the STOP signal appears, after which the bus is considered idle
Data Format
- Each byte transmitted on the SDA line must be 8 bits long. Each transmission can contain any number of bytes.
- Each byte must be followed by an acknowledge bit.。
- Data is transmitted withthe most significant bit (MSB) first.
- If the target device cannot immediately receive or send another complete byte of data due to operations such as handling internal interrupts, it**can put the controller into a wait state by pulling the SCL line low.**When the target device is ready to receive the next byte of data,releasing the SCL line allows data transmission to continue.。

Acknowledge and Not Acknowledge Signals
- The acknowledge signal occurs after each byte transmission. The acknowledge bit allows the receiving device to indicate to the transmitting device that the byte has been successfully received and the next byte can be sent.
- Acknowledge Signal: Whenthe transmitting device releases the SDA line during the 9th clock pulse,, The receiving device can pull the SDA line low and keep it stable low during this clock high level.
- Non-acknowledge signal:During the 9th clock pulse, the SDA line remains high., the controller can generate a stop signal to terminate the transmission, or a repeated start signal to begin a new transmission.

The five situations that cause a NACK signal include:
- No receiving device on the bus responds to the transmitted address.
- The receiving device is busy with other real-time functions and cannot start communication.
- The receiving device receives data or commands it cannot understand during the transmission.
- The receiving device cannot receive any more data bytes.
- The controller-receiver must indicate the end of transmission to the target transmitter.
Read/write direction
Data transfer format: first,send a 7-bit target address,, followed by a read/write direction bit (R/W bit).。
The read/write direction bit is bit 8,0 indicates a write operation (WRITE),1 indicates a read operation (READ)。
Data transmission is alwaysterminated by a stop condition (P) generated by the controller. However,if the controller needs to continue communicating on the bus, it can generate a repeated start condition (Sr) to address other target devices without first generating a stop condition。
This allows various combinations of read/write formats to be implemented within the same transmission session

I2C Waveform
Write Operation

Before data transmission, the master must firstsend a start condition, which is a transition of the SDA line from high to low while the SCL line remains high, corresponding to the part shown in the figure; the logic analyzer software also marks it with a green dot.
The write operation can be divided into the following steps:
- The master sends a start signal
- The master sends the I2C peripheral address and write operation, waiting for an acknowledgment signal
- The slave sends an acknowledgment signal
- The master sends the register address, waiting for an acknowledgment signal
- The slave sends an acknowledgment signal
- The master sends the data to be written to the register, waiting for an acknowledgment signal
- The slave sends an acknowledgment signal
- The master sends a stop signal; if writing multiple registers, repeat steps 6 and 7
Read operation
Whether it is a read or write operation, the I2C peripheral address must be written first, so the initial waveform is the same

The read operation can be divided into the following steps:
- The master sends a start signal
- The master sends the I2C peripheral address and write operation, waiting for an acknowledgment signal
- Slave sends an acknowledgment signal
- Master sends the address of the register to be read and waits for an acknowledgment signal
- Slave sends an acknowledgment signal
- Master sends a start signal
- Master sends the address of the I2C peripheral to be read and a read operation, waiting for an acknowledgment signal
- Slave sends an acknowledgment signal
- Slave sends data, i.e., the data of the register to be read, waiting for an acknowledgment or non-acknowledgment signal
- If the master is not reading data, it sends a non-acknowledgment signal; if it continues reading, it sends an acknowledgment signal
I2C subsystem framework
Hierarchical structure

I2C device driver layer
The main role of the I2C device driver layer is to write drivers,so that the I2C peripheral can work properly, and thenCreated the corresponding device node, providing a standardized interface that allows upper-layer applications to easily interact with I2C devices.
Specifically, the I2C device driver layer includes the following key parts:
- i2c_client
- Represents a slave device connected to the I2C bus
- Contains information such as the slave device’s address and its associated I2C adapter
/dev/i2XDevice Node- Provides an interface for upper-layer applications to access the device
- By opening, reading/writing, and controlling the device node, applications can interact with I2C devices
- The kernel I2C subsystem is responsible for forwarding application operations to the corresponding i2c_driver
i2c_driver- Implements the driver for a specific I2C slave device
- Responsible for device initialization, reading/writing, configuration, and other operations
- Interacts with the device via i2c_client
- Provides a standardized interface for device access to the upper layer
- I2C Bus Subsystem
- Manages the entire I2C bus, including registering/unregistering I2C adapters and slave devices
- Coordinate i2c_client and i2c_interaction between drivers
- Provide a unified I2C access interface for the upper layer
I2C core layer
The I2C core layer is located between the I2C device driver layer and the I2C adapter driver layer, serving as a bridge that connects the two, responsible for data transfer between the I2C device driver layer and the I2C adapter driver layer. The main functions of the I2C core layer are
i2c_master_sendi2c_master_recvi2c_transfer
i2c_master_sendandi2c_master_recvThese two functions are responsible forgenerating timing and data frames that comply with the I2C protocol, and perform actual bus operations through the corresponding I2C adapter driver.
Among them,
i2c_master_sendandi2c_master_recvfunctions are the basic read and write interfaces provided by the I2C core layer.
i2c_master_sendis used to send data to the I2C slave device,i2c_master_recvis used to receive data from the slave device.They respectively accept the following parameters:
struct i2c_client *client: Pointer to the target I2C slave deviceconst char *buf/char *buf: Data bufferint count: Number of bytes to send/receive
Andi2c_transferfunction is a more comprehensive I2C transfer function,i2c_master_sendandi2c_master_recvfunctions actually call thei2c_transfer
i2c_transfer function, which accepts the following parameters:
struct i2c_adapter *adap: Pointer to the target I2C adapterstruct i2c_msg *msgs: Pointer to an array of I2C messagesint num: Number of messages in the message array
Since the corresponding device node has been created in the I2C device driver layer, with the driver, one can directly operate the specific I2C hardware. However, the I2C subsystem is not implemented this way; instead, it adds the I2C core layer and the I2C adapter driver layer. So why is it designed this way?
The main reason is that through driver layering, it cansolve the problem of multiple applications simultaneously accessing the same I2C device causing conflicts, in addition, through thismodular design, the reusability and maintainability of the code can be improved, allowing the I2C core layer and device drivers to be developed and upgraded independently, and the I2C adapter driver can also be optimized for different hardware platforms.
I2C adapter driver layer
The I2C adapter driver layer is another important component of the I2C subsystem, responsible forimplementing the driver for specific I2C hardware controllers. The functions of the I2C adapter driver are as follows:
- Provide a standardized I2C transmission interface for the I2C core layer to call
- Implement timing control and data transmission/reception of the I2C bus protocol
- Manage slave devices on the I2C bus
- Handle I2C bus errors and abnormal situations
I2C client code writing
struct i2c_client
1 | /** |
Device tree representation of I2C Client
Inrk3568.dtsithe device tree, there are device tree nodes for I2C0, I2C1, I2C2, I2C3, I2C4, and I2C5. Here, only the device tree node for I2C1 is listed, as follows:
1 | i2c1: i2c@fe5a0000 { |
i2c1: i2c@fe5a0000The node represents the I2C1 controller. If an I2C peripheral is mounted on I2C1, you can directly add a child node for the I2C peripheral under the I2C1 controller node. The device tree node for FT5X06 is inkernel/arch/arm64/boot/dts/rockchip/topeet-screen-lcds.dtsas shown below:
1 | &i2c1 { |
This node appends the FT5X06 touch chip related node to the I2C1 controller node. That is,ft5x06 as a child node of i2c1
Cancel the original driver of rk3568
- Uncheck the FT5X06 driver in menuconfig
- In
topeet-screen-lcds.dtsselectLCD_TYPE_MIPI
1 |
|
- Set the status of the original ft5x06 device tree node from okay to disabled
1 |
|
FT5X06 Client Device Tree Writing
1 | &i2c1 { |
The description of the appended device tree node is as follows:
&i2c1: Indicates a reference to I2C controller 1.status = "okay";: Indicates enabling I2C controller 1.myft5x06: my-ft5x06@38: Defines a device node named myft5x06.my-ft5x06@38Indicates that the I2C address of this device is 0x38.compatible = "my-ft5x06";: This attribute is used to identify the device type, here indicating that it is a device namedmy-ft5x06.reg = <0x38>;: This attribute defines the device’s address on the I2C bus, here it is 0x38
In addition to the I2C part, the FT5X06 touch chip also has two other GPIOs: an interrupt pin and a reset pin. The function matching table for each pin is shown below

Therefore, we also need to describe this in the device tree
1 | &i2c1 { |
reset-gpio = <&gpio0 RK_PB6 GPIO_ACTIVE_LOW>;: Defines the reset pin of the device, connected toGPIO0the RK_PB6 pin, with active low level.interrupt-parent = <&gpio3>;: Specifies the interrupt parent node as GPIO3.touch-gpio = <&gpio3 RK_PA5 IRQ_TYPE_EDGE_RISING>;: Defines the touch pin of the device, connected to the RK_PA5 pin of GPIO3.interrupts = <RK_PA5 IRQ_TYPE_LEVEL_LOW>;: Further describes the interrupt trigger method, which is low-level trigger.pinctrl-names = "default";andpinctrl-0 = <&myft5x06_pins>;: Specifies the default pin configuration used by the device.
The pinctrl node specified here is namedmyft5x06_pins, so the pinctrl node needs to be appended. The appended content is as follows:
1 | &pinctrl { |
Writing I2C Client in C
Generally, the device tree is used to write I2C Client, but before the introduction of the device tree, using C files is also acceptable
struct i2c_adaper
1 | /* |
i2c_get_adapter()
i2c_get_adapterThe main function of this function is to search for the correspondingi2c_adapter_idrstructure from the given I2C adapter number nr in thei2c_adapterstructure, which is defined in thedrivers/i2c/i2c-core-base.cfile, with the specific content as follows
1 | struct i2c_adapter *i2c_get_adapter(int nr) |
i2c_put_adapter()
1 | void i2c_put_adapter(struct i2c_adapter *adap) |
When the driver is unloadedi2c_adapterThe structure needs to be freed, and when the structurei2c_put_adapterfunction is used to freei2c_adapterstructure,i2c_put_adapterThe function is also defined indrivers/i2c/i2c-core-base.cthe file
i2c_new_client_device()
i2c_new_client_devicefunction is used to create and register a device corresponding to the I2C bus. After registration, the I2C subsystem automatically creates the corresponding device node for the device, allowing upper-layer applications to access and control it. This function is also defined indrivers/i2c/i2c-core-base.cthe file
1 | /** |
i2c_bus_probe()
i2c_new_client_device()inclient->dev->busis assignedi2c_bus_type, executei2c_driverwill be executed before the probe ofi2c_bus_probe(). Here, the irq of the client is assigned
1 | struct bus_type i2c_bus_type = { |
struct i2c_board_info
1 | // include/linux/i2c.h |
Example
1 |
|
Core layer I2C communication
The main functions of the I2C core layer arei2c_master_send、i2c_master_recvandi2c_transfer, wherei2c_master_sendandi2c_master_recvfunctions are the basic read/write interfaces provided by the I2C core layer. These two functions are responsible for generating timing and data frames that comply with the I2C protocol, and perform actual bus operations through the corresponding I2C adapter driver. The two functions are defined ininclude/linux/i2c.hthe file
i2c_master_recv()
1 | // include/linux/i2c.h |
i2c_master_send()
1 | /** |
i2c_transfer_buffer_flags()
i2c_master_recv()andi2c_master_send()actually callsi2c_transfer_buffer_flags()this function
1 | // drivers/i2c/i2c-core-base.c |
This function is used to transmit data via the I2C bus. It first constructs ai2c_msgstructure, describing the current transmission operation, including the slave device address, transmission flags, data length, and data buffer. Then it callsi2c_transferfunction to perform the actual data transmission.
i2c_transfer()
1 | /** |
struct i2c_adapter *adap: Indicates the I2C adapter to be used. Each I2C controller corresponds to ai2c_adapterstructure, which contains various attributes and operation functions of this adapter.struct i2c_msg *msgs: Points to an array of i2c_msg structures, used to describe one or more I2C messages to be transmitted.
struct i2c_msg
1 | // include/uapi/linux/i2c.h |
According toi2c_master_sendthe function’s input parameters, it can be inferred that 0 indicates a write operation.
struct i2c_algorithm
i2c_transferThe function itself does not have the ability to control hardware; in fact,master_xferis the function that actually drives the hardware to work, thereby achieving I2C communication,master_xferdefined ini2c_adapterof the structi2c_algorithm In the struct, the specific content is as follows:
1 | /** |
master_xferandsmbus_xferBoth functions are hardware control functions at the I2C device driver layer, written by the original manufacturer’s engineers. The implementation functions for rk3568 are defined indrivers/i2c/busses/i2c-rk3x.cfile. In general, you only need to usei2c_transferfunction indirectly.
__i2c_lock_bus_helper
i2c_transfercall the function that actually performs the transmission in__i2c_transferbefore calling__i2c_lock_bus_helper
1 | /* |
The purpose of this code is to manage access to the I2C bus through appropriate locking mechanisms, ensuring system stability and preventing race conditions between multiple operations.
- Ifcurrently in an atomic context or interrupts are disabled, in both cases,the kernel usually does not allow operations that may cause context switches。
the code usesi2c_trylock_bus()function to attempt to acquire the I2C bus lock.I2C_LOCK_SEGMENTis used to specify the lock flag. Ifi2c_trylock_bus()returns failure (return value is false), indicating activity on the I2C bus, the function returns error code-EAGAIN, indicating that the lock cannot be acquired temporarily.
- If not in an atomic context or interrupts are disabled
the code directly callsi2c_lock_bus()function to acquire the I2C bus lock without conditional checks. This is because in this case, the system allows operations that may cause context switches.
__i2c_transfer
1 | /** |
__i2c_transferthe current timestamp will be recordedorig_jiffies, and loop up toadap->retriesretries. During each retry, call the adapter’smaster_xferfunction to complete the I2C transfer. If the return value is not-EAGAIN(indicating an arbitration loss error), or if timeout has occurred, exit the loop.
I2C driver
i2c_add_driver()
1 | // include/linux/i2c.h |
Specific implementation:
1 | // drivers/i2c/i2c-core-base.c |
The main function of this function is to register the I2C device driver into the driver core and initialize the related data structures. The data structure type passed here isi2c_driver, which needs to be filled in when writing the driver. This structure is defined in theinclude/linux/i2c.hheader file, and its specific content is as follows:
struct i2c_driver
1 | // include/linux/i2c.h |
When callingi2c_add_driverBefore registering an I2C device with the function, you need to first fill in thei2c_driverstructure, and then implement various callback functions, which is the same as the platform bus content explained earlier.
Example
1 |
|
Using I2C in applications
ioctl controls I2C
ioctl is an interface function used in device drivers to control devices. In applications, ioctl can be used to control the I2C controller to read and write I2C devices. The I2C controller nodes of RK3568 are as follows:
The control command CMD for the I2C controller is defined ininclude/uapi/linux/i2c-dev.hthe file:
1 | /* /dev/i2c-X ioctl commands. The ioctl's parameter is always an |
i2c_rdwr_ioctl_dataThis structure is used inI2C_RDWRioctl call to pass I2C messages. Where
msgsis a pointer toi2c_msgan array of structures, used to store one or more I2C messages.nmsgsisi2c_msgthe length of the structure array, i.e., the number of I2C messages.
Example
1 |
|
generic I2C driver
the generic I2C driver file isdrivers/i2c/i2c-dev.c, which provides a unified driver framework for I2C peripherals, divided into I2C client and I2C driver.
it provides a generic device node for upper-layer applications/dev/i2c-X(X represents the I2C bus number).
The application can directly open this device node/dev/i2c-X, and use standard I/O operations such as open(), ioctl(), read(), write(), etc., to communicate with the I2C slave device.
This driver is generally enabled by default, and the specific path is as follows:
1 | Device Drivers |
i2c_dev_init()
Driver initialization function
1 | static int __init i2c_dev_init(void) |
i2c_dev_adapter()
i2c_dev_init()Called last in the functioni2cdev_attach_adapter()Function to bind an existing i2c adapter to an i2c device
1 | static int i2cdev_attach_adapter(struct device *dev, void *dummy) |
The function of this function is to create a corresponding character device node for a new i2c adapter discovered on the system bus.
struct file_operations i2cdev_fops
i2c_devThe file operations structure specified by the cdev field in the structure isi2cdev_fops, and its specific content is as follows:
1 | static const struct file_operations i2cdev_fops = { |
i2cdev_open()
1 | static int i2cdev_open(struct inode *inode, struct file *file) |
i2cdev_read()
1 | /* |
i2cdev_write()
1 | static ssize_t i2cdev_write(struct file *file, const char __user *buf, |
i2cdev_ioctl()
1 | static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
Test example
1 |
|
I2C_tools
Compile:
1 | make CC=/home/topeet/Linux/linux_sdk/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarc |
If using Ubuntu or Debian system, simply use the commandsudo apt install i2c-toolsto install
i2cdetect
i2cdetect can be used to detect and probe devices connected to the I2C bus.
i2cdetect -V: Output version information

i2cdetect -l:List all I2C buses

i2cdetect -FQuery the set of functions supported by devices on the bus, for examplei2cdetect -F 1will list the functions supported by devices on bus 1

i2cdetect -aScan all I2C device addresses in the range 0x00 to 0xFF on the bus. For example:i2cdetect -a -y 1will scan all I2C device addresses on the I2C1 bus

Here, 0x38 is the I2C device address of the FT5X06 touch chip
i2cdump
i2cdump can read the values of all registers on the device. The specific usage is as follows:
i2cdump -VView version number

i2cdump -f -aRead device registers, usei2cdump -f -a 1 0x38command can read all register values (from 0x00 to 0xFF) of the I2C device with address 0x38.-foption is used to force the use of the device address-aoption is used to read the entire address range.

i2cdump -f -rSpecify a register range to read, usei2cdump -f -r 0x80-0xff 1 0x38The command can read only the register values in the range 0x80 to 0xff of the I2C device with address 0x38. The -r option is used to specify the register address range to read.

i2cset
The i2cset command is used to write data to a specific register of an I2C device. Its usage is as follows:
1 | i2cset -f -r 1 0x38 0x80 0x11 |
This command means:
- Forcefully use device address 0x38 on I2C bus 1
- Write the value 0x11 to register address 0x80 of the device
After writing, the command returns a confirmation message indicating whether the write was successful. If the write fails, it returns an error message.
i2cget
The i2cget command is used to read data from a specified register of an I2C device. Its usage is as follows:
1 | i2cget -f 1 0x38 0x80 |
This command means:
- Forcefully use device address 0x38 on I2C bus 1
- Read data from register address 0x80 of the device
This command returns the value of register 0x80. Upon successful reading, it displays a hexadecimal value like 0x11. If the read fails, it returns an error message.
i2ctransfer
i2ctransfer is a more powerful and flexible I2C operation tool. Compared to the previously introduced i2cset and i2cget commands, it can perform read and write operations in a single command. The specific usage is as follows:
Write operation
1 | i2ctransfer 1 w2@0x38 0x80 0x22 |
1Indicates that the I2C bus number for the operation is 1w2Indicates writing 2 bytes of data@0x38Indicates that the device address is 0x380x80Indicates that the register address to be written is 0x800x22Indicates that the value to be written to the register is 0x22
Read operation
1 | i2ctransfer 1 w1@0x38 0x80 r1 |
1Indicates that the I2C bus number for the operation is 1w1Indicates writing 1 byte of data@0x38Indicates that the device address is 0x380x80Indicates that the register address to be written is 0x80r1Indicates reading 1 byte of data
Software I2C
Using GPIO to simulate I2C driver
Since software I2C is to be used, the hardware I2C1 must be disabled in the device tree. In the device tree, set theft5x06andi2c1’sstatusboth todisabled。
At this point, the two multiplexed pins GPIO0 B3 and GPIO0 B4 of I2C1 will be set to the default GPIO function.
1 |
|
Use the Linux default simulated I2C program
1 | export ARCH=arm64 |
Device tree modification
1 | i2c6:i2c6@gpio { |
Append to the I2C6 node, adding content related to the FT5X06 touch chip as follows:
1 | &i2c6 { |
Note that since the previously written device tree node is also named myft5x06, a naming conflict will occur, so the previously written myft5x06 device tree node needs to be commented out
Write the driver as follows:
Removed GPIO-related content from the original driver code
1 |
|
SMBus
SMBus (System Management Bus) is a serial bus protocol based on the I2C bus, released by Intel in 1995. It was originally designed to connect smart batteries and other system management devices within computer systems.
SMBus is very similar to the I2C bus; both use two-wire serial communication. SMBus uses SMBDAT and SMBCLK as the data and clock lines, which are very similar to I2C’s SDA and SCL, as shown in the following figure:

SMBus features
The main features of SMBus are as follows:
Electrical characteristics:
- Uses open-drain output, requiring external pull-up resistors
- Voltage range: 0V to 5.5V
- Maximum clock frequency: 100kHz
Communication Protocol:
- Master-slave communication, one master controls multiple slaves
- Address space: 7-bit or 10-bit
- Supports read/write operations
- Supports block transfer and byte transfer
- Supports multiple transaction types, such as quick command, write byte, read byte, etc.
Functional Features:
- Simple, low-cost, low-power
- Aimed at system management applications, such as power management, temperature monitoring, etc.
- Highly compatible with I2C, can reuse I2C hardware
Timing Characteristics:
- Start and Stop conditions are the same as I2C
- Address and data transfer timing is also similar to I2C
- But there are some special timings, such as quick command, block transfer, etc.
Differences between SMBus and I2C
- Speed Range:
I2C supports a speed range from 10kHz to 3.4MHz, covering a wider range of application scenarios. SMBus, on the other hand, only supports a speed range from 10kHz to 100kHz, primarily targeting low-speed system management applications. - ACK Response:
I2C does not mandate that the slave send an ACK response, which improves flexibility. However, if the slave does not respond, the master may generate an error. SMBus requires the slave to send an ACK response, ensuring the master can detect the slave’s presence and avoid misoperations. - Time Limit:
SMBus specifies that the slave cannot hold the SCL line low for more than 35ms, otherwise the ongoing communication will be reset. I2C has no such time limit, and the master and slave can control the SCL line state autonomously. - Other Differences:
SMBus has some commands and transaction types specifically designed for system management, such as quick commands and block transfers. SMBus has a smaller address space compared to I2C, supporting only 7-bit or 10-bit addresses. SMBus also has some differences in electrical characteristics, such as voltage range.
SMBus Bus Software Implementation
In the Linux kernel,I2C and SMBus share a common bus architecture,managed and abstracted through the i2c-core subsystem, in thei2c.hheader file, it is defined in the i2c_algorithmstructure, with the specific content as follows:
1 | /** |
Among them, the smbus_xfer function is used to implement some SMBus-specific operations, such as quick commands, write byte, read byte, etc.
When the I2C controller works in SMBus mode, it executes SMBus special transactions through the smbus_xfer function.
SMBus Bus API Functions
i2c_smbus_read_byte(const struct i2c_client *client)- SendSMBus Read Byteprotocol.
- Do not send the register address; directly read the data byte currently pointed to by the device.
- Applicable to: devices that support auto-incrementing addresses (such as continuous reading of certain EEPROMs), or the address pointer has been set previously by other means.
- ⚠️ Most modern I2C devicesdo not supportthis addressless read, soit is rarely used。
i2c_smbus_write_byte(const struct i2c_client *client, u8 value)- SendSMBus Send Byteprotocol.
- Send only one byte (
valueas a command),No data phase。 - Note: The
valueis treated ascommand, not as the data to be written! - Commonly used to trigger device actions (such as reset, start conversion),not for writing register values。
- ❗Easily misunderstood! If you want to ‘write a value to a register’, use the
i2c_smbus_write_byte_data。
i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command)- SendSMBus Read Byte Dataprotocol.
- First send
command(register address), then read back 1 byte of data. - ✅ The most common way to read a register。
i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8 value)- SendSMBus Write Byte Dataprotocol.
- Send
command(register address) +value(data to be written). - ✅ The most common way to write to a register。

