Linux Kernel

I2C Overview in Linux

In this article we will explore basic introduction on I2C and implementation of I2C in Linux. We will explore the I2C Subsystem in Linux kernel and some examples for I2C master and slave communication.

Description

I2C stands for Inter Integrated Circuit, is a on board protocol for used for Communication between two ICs. It is a serial, two wire protocol. This follows the master slave mode. I2C master always initiates the communication and Clock for communication is also provided by I2C master. On two lines there can be multiple devices connected. On single master and many slave configuration, every slave will be distinguished with unique slave address.

Example configuration of single master and multiple slaves:

In the block diagram above, we can see there is single master and 3 slaves with addresses as mentioned in the box of each slave.

I2C Protocol

General I2C messages sequence used in communication between master and slave is shown below:

Start -> Address + R/W byte -> Ack -> Data byte1 -> Ack -> Data byte2 -> Ack -> Data byte3 -> Ack -> Stop

Start: Condition generated by master to indicate it wants to communicate with the slave.

Address + R/W byte: 7-bit slave address and 1 bit to indicate whether the operation is read or write.

Ack: Acknowledgement is always sent for every byte transfer. This is sent by the receiving device.

Stop: Once the transfer is complete, controller/master will send the stop condition to complete the transfer.

In Linux, I2C drivers are organized into three logical layers:

  1. master/adapter driver
  2. I2C-core layer
  3. slave/client driver

I2C Master/Adapter Drivers

These are located in the kernel source tree at the path: drivers/ I2C /busses/. For every I2C master or controller, there should be a driver present in this path. This is the driver which registers itself into the I2C-core layer and controls/manages the I2C-buses. This is the driver which communicates with the I2C slave devices over I2C busses present on the platform.

I2C-Core Drivers

This is the common I2C core logic of Linux. This is common and independent of any specific I2C master or slave. This is the core implementation of I2C subsystem in Linux.

I2C Slave/Client Driver

This is the slave chip driver needed for every slave device. Any I2C slave device should have driver or implementation present in this category. This is required for any slave device to register itself to the Linux I2C subsystem.

Enabling the I2C Drivers in the Linux Kernel

I2C core related implementation will be enabled with I2C Core kernel config flags. I2C Master driver will also be enabled with the specific board I2C controller. Similarly, there will be one config flag should be enabled for I2C slave driver.

All the required configurations can be enabled in two ways. One as built-in driver or as a kernel module. Kernel modules provides us the advantage of loading it as runtime without changing or compiling the Linux kernel.

Module approach can only be used if the device access is not part of the boot path. If any device’s data is needed for the system to boot, then these drivers need to be built in. Such drivers cannot be compiled as a dynamically loaded modules at runtime.

Instantiating the I2C Devices

In Linux, different ways are present to instantiate the I2C devices. Two widely used methods are: static and dynamic

Static: on ARM systems, device tree can be used to create an instance of the I2C device.

Specific device node can be added in the device tree. Example, device tree declaration for I2C device is:

i2C0: i2C@60000000 {

        eeprom@50 {
                compatible = "atmel,eeprom-at";
                reg = <0x50>;
        };

        rtc@60 {
                compatible = "rtc,rtc-maxim";
                reg = <0x60>;
        };
};

Above example creates an instance of 2 I2C slave devices. One is EEPROM device and another one is RTC device. After the system comes up, these entries can be found in /sys/bus/I2C/devices/I2C-0/. Both will be created inside I2C-0 directory because these are placed inside I2C node 0.

Dynamic: Runtime instance of the I2C device can be created through sysfs files.

There are two sysfs files present for every I2C bus. new_device and delete_device, both files are write-only and I2C slave address can be written on these files to create device instance and delete device instance.
To create an I2C device equivalent to the devices defined in device tree as of previous example.

Create EEPROM instance with slave address 0x50:

# echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-0/new_device

Deletion of EEPROM device instance:

# echo 0x50 > /sys/bus/i2c/devices/i2c-0/delete_device

Next, probing of the device with driver can also be done from sysfs files: there are two write-only files bind and unbind, associated with every driver. Exporting device id to the bind and unbind files results to the linking and unlinking of the driver with the device. For instance, driver rtc-ds1307 have the below files in the sysfs as discussed earlier.

[root]$ ls /sys/bus/i2c/drivers/rtc-ds1307/
bind    uevent  unbind
[root]$

Let us have a little more discussion on the sysfs files of I2C subsystem:

I2C sysfs is present at the location: /sys/bus/I2C/

Snapshot of the I2C sysfs:

As we can see, there are two directories: devices and drivers

Devices will contain all the device instances present and known to the Linux kernel. On our board, we have below I2C devices inside the devices’ directory:

Drivers will contain all the I2C drivers present and known to the Linux kernel. On our board, we have below I2C drivers inside the drivers’ directory:

For bind and unbinding of the devices with drivers, there are two write-only files present inside every driver. For the linking of any device with the driver can be done by echoing the device id to the bind file and unlinking can be done by echoing the device id to the unbind file.

Binding of the I2C Device with I2C Driver

[root]$ echo 1-0068 > /sys/bus/i2c/drivers/rtc-ds1307/bind
[592061.085104] rtc-ds1307 1-0068: registered as rtc0
[root]$

Confirmation of the successful binding can be done by checking the soft link created after the bind operation. A new device soft link can be seen in the below log instance after executing the command mentioned in the binding section:

[root]$ ls /sys/bus/i2c/drivers/rtc-ds1307/
1-0068  bind    uevent  unbind
[root]$

Unbinding of the I2C Device with the I2C Driver

[root]$ echo 1-0068 > /sys/bus/i2c/drivers/rtc-ds1307/unbind

Confirmation of the successful unbinding can be done by checking the soft link device node created previously inside the drivers’ directory will be removed. If we check the content of the drivers’ directory, we should see the logs snapshot as below:

[root]$ ls /sys/bus/i2c/drivers/rtc-ds1307
bind    uevent  unbind
[root]$

I2C Applications or Use Cases with Respect to Linux

  1. EEPROM device to store small data, memory is of few KBs.
  2. RTC Device, used to keep the real time data. Device is used to keep the track of time even when the main system is power off.
  3. Many HW sensor devices like thermal sensors, current sensors and voltage sensors comes as an I2C devices.
  4. FAN Controlling chips also comes as I2C devices.

I2C-tools

User space applications in Linux environment are used to access I2C slave devices. I2Cdetect, I2Cget, I2Cset, I2Cdump and I2Ctransfer are the commands available when I2C-tools are installed on any Linux platform. All the devices’ use cases discussed in the I2C applications sections can be accessed through these tools.

There is no need of I2C slave device driver, when trying to access the slave device with I2C-tools. These tools can allow us to access the devices in raw format. More details on these utilities are available in another article.

Conclusion

We discussed the I2C subsystem in Linux. I2C framework with logical code organization overview was provided. We also discussed I2C sysfs files. We have discussed the I2C communication message sequence. We have gone through the device instance creation in both the ways, i.e., static and dynamically. We also explored the bind/unbind drivers with devices. Some of the I2C real time applications.

About the author

Sushil Rathore

Sushil Rathore is having hands-on experience in Linux Platform SW. He's an expert of Linux on ARM/X86 Boards. He has very good understanding on Bootloaders and other platform softwares. He has good industrial experience and have worked in reputed Organizations. Currently he is associated with a reputed firm in the networking domain.