Linux Kernel

Linux I2C Master Driver

We will discuss about the I2C master/controller/adapter driver in Linux. Generally, Linux I2C master controller driver registers the I2C controller in the Linux I2C subsystem. The I2C master driver is required to let the Linux OS know that there is a controller that exists. We will discuss this in detail and we will also check on the master side implementation of the I2C driver.

Description:

In any of the SOC, I2C is the common bus provided for onboard devices like eeprom, rtc, etc. This bus is used to connect the slow and small devices on the platform. Any communication between the I2C slave devices over I2C bus is done by the master side implementation on the SOC. There is a hardware controller that is present on the SOC which implements the I2C communication in SOC. To enable this I2C HW controller, we need the I2C master driver in Linux. The main function of I2C master driver is to program the hardware registers for I2C communication. At the same time, expose the standard functions to the Linux OS for I2C as per the Linux I2C framework.

Linux defines the standard implementation for the I2C master driver. For any new SOC which has the new controller that is not known to the Linux, one must write the I2C master or adapter driver.

This I2C master driver registers the new bus to the user space for communication. The I2C master must provide one bus, but it can provide more than one bus as well.

These drivers are present inside the drivers/I2C/busses/ in the kernel source.

Let us take an example of the I2C-i801 driver which is present inside the drivers/I2C/busses.

First, let us understand a little bit on I2C-i801. It is the I2C/SMBus bus controller that is implemented on Intel Platforms. The I2C-i801 controller is accessed internally over PCI. Hence, it is implemented as the PCI driver.

On any other platform controllers, it could be memory mapped. Hence, simply accessing the physical memory controller registers can be accessed. These details can be fetched from the hardware datasheet of the controller. In such case, this can be implemented as platform driver; the same can be seen in the already available I2C bus drivers.

Let us focus on the example of the I2C-i801 driver.

The “i2c_adapter” struct is the structure which needs to be instantiated in Linux for the new bus registration.

Few important fields are a must, and we should provide the values to these fields.

The adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; // is the class of the I2C devices. Linux defines various classes for the I2C devices that need to select the class which are supported by this controller.

The adapter.algo = smb_algorithm;  // field defines the types of the I2C/SMbus function that is supported by the I2C controller.

This field consists of reference of the object to the “struct i2c_algorithm” that is defined by the Linux I2C SMBus framework.

static const struct i2c_algorithm smb_algorithm = {
    .smbus_xfer = access,
    .functionality  = func,
};

 

The “.smbus_xfer” is the callback to the function which is used to program the hardware registers for the I2C/SMBus access. This will be the lower level function for the hardware access.

The “.functionality” is the type of the I2C/SMBus functions that is supported by this controller driver.  Few examples of the Macros which can be used in this field are as follows:

I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL

These are the macros which can be used as per the functionality that is supported by our controller.

Once all these fields are populated as per our hardware controller, we should call the “i2c_add_adapter()” Linux function with the populated field. Once successful, this function creates a new I2C bus for the user space to access the I2C devices that are connected on the I2C bus.

Now, we can consider another example of the I2C master driver (drivers/i2c/busses/i2c-altera.c) if we go through the implementation of this I2C master driver. We should find the similar implementation that we previously discussed.

Since this driver is working for the controller which is accessed through the memory mapped IO, the implementation is done as the platform driver.

On checking the probe of this master driver, we can see that the following fields are populated which we discussed in the case of the I2C-i801 driver:

idev->adapter.owner = THIS_MODULE;

idev->adapter.algo = &altr_i2c_algo;

In this driver, we can also see that the “i2c_algorithm” struct is populated with the similar fields as in the case with the i2c-i801 driver.

static const struct i2c_algorithm altr_i2c_algo = {
    .master_xfer = altr_i2c_xfer,
    .functionality = altr_i2c_func,
};

 

The difference to note here is that in altera driver, the “.master_xfer” function is used but it uses the “.smbus_xfer” earlier. The reason for this is that Intel’s I801 controller is actually the SMBus controller; it doesn’t support all the I2C functionality. It supports the SMBUs completely but the I2C functions are limited. But in case of the Alter I2C controller, it supports the I2C functions.  The functions supported by altera controller are I2C_FUNC_I2C and I2C_FUNC_SMBUS_EMUL.

In altera driver as well, after populating the required fields, there is a call to the i2c_add_adapter ().

Now, let us look at the userspace level what happens after the successful addition of the I2C adapter driver to Linux.

There are a set of I2C-util commands available which can be used to check if the I2C bus is created successfully. The “i2cdetect” is the command that can be used to detect the existing/known busses in the Linux system. The “i2cdetect –l” option should list all the busses; we should see our bus adapter that is also listed there in the output. From the logs, we can get the bus number and device node information that can be determined. The other way to check the “sysfs” files for the new bus is created by our adapter. The /sys/bus/i2c/devices is the path which can be checked for the busses which are present in our system.

Our newly created adapter can be checked by reading the “name” file at the /sys/bus/i2c/devices/i2c-x/name path.

X = 0, 1, 2, etc.

The example output from the system that we have is as follows. This system has two I2C busses:

$ ls /sys/bus/i2c/devices/
i2c-0  i2c-1

 

The following is the example output of reading the name file:

$ cat /sys/bus/i2c/devices/i2c-0/name
SMBus I801 adapter at e000

 

The previous example output clearly shows that i2c-0 belongs to the i801 driver and all the devices that are connected to this bus can be accessed through i2c-0.

Let us understand how the probe of our adapter driver is called. The probe function is very important as we are doing all the stuff that we previously discussed inside the probe function.

In case of i801 driver, as it is the pci driver, if the system contains the PCI device with the device and vendor ID as supported by the i801 driver, the probe of the functions is executed automatically. The following is the snapshot from the driver which shows a few PCI device and vendor IDs:

In case of alter driver, it is the controller on the ARM device probably, so the device tree note needs to be created. The device tree entry is needed for the master driver which is used to create the device instance. Once the driver’s compatible string matches with the node’s compatible string, the probe function is called. The following is the snapshot from the driver for the compatible string that we discussed:

Conclusion

So far, we discussed about the I2C master driver with the help of two example I2C controllers (i801 and Altera). We discussed on the minimal fields that are required for the new I2C bus registration. We discussed the i2c-i801 and i2c-alter drivers as an example and browsed their code during the discussion. We tried to find out the common or simple flow that is needed for the I2C master/adapter/bus driver implementation in Linux. With the help of this information, one should be able to understand the I2C adapter driver and their offerings to the Linux OS.

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.