Introduction to I2C and SMBus¶
I²C (pronounce: I squared C and written I2C in the kernel documentation) is a protocol developed by Philips. It is a slow two-wire protocol (variable speed, up to 400 kHz), with a high speed extension (3.4 MHz). It provides an inexpensive bus for connecting many types of devices with infrequent or low bandwidth communications needs. I2C is widely used with embedded systems. Some systems use variants that don’t meet branding requirements, and so are not advertised as being I2C but come under different names, e.g. TWI (Two Wire Interface), IIC.
The latest official I2C specification is the «I2C-bus specification and user manual» (UM10204) published by NXP Semiconductors. However, you need to log-in to the site to access the PDF. An older version of the specification (revision 6) is archived here.
SMBus (System Management Bus) is based on the I2C protocol, and is mostly a subset of I2C protocols and signaling. Many I2C devices will work on an SMBus, but some SMBus protocols add semantics beyond what is required to achieve I2C branding. Modern PC mainboards rely on SMBus. The most common devices connected through SMBus are RAM modules configured using I2C EEPROMs, and hardware monitoring chips.
Because the SMBus is mostly a subset of the generalized I2C bus, we can use its protocols on many I2C systems. However, there are systems that don’t meet both SMBus and I2C electrical constraints; and others which can’t implement all the common SMBus protocol semantics or messages.
Terminology¶
Using the terminology from the official documentation, the I2C bus connects one or more master chips and one or more slave chips.
A master chip is a node that starts communications with slaves. In the Linux kernel implementation it is called an adapter or bus. Adapter drivers are in the drivers/i2c/busses/ subdirectory.
An algorithm contains general code that can be used to implement a whole class of I2C adapters. Each specific adapter driver either depends on an algorithm driver in the drivers/i2c/algos/ subdirectory, or includes its own implementation.
A slave chip is a node that responds to communications when addressed by the master. In Linux it is called a client. Client drivers are kept in a directory specific to the feature they provide, for example drivers/media/gpio/ for GPIO expanders and drivers/media/i2c/ for video-related chips.
For the example configuration in figure, you will need a driver for your I2C adapter, and drivers for your I2C devices (usually one driver for each device).
I2C Utilities in Linux
In Linux environment few commands are available, which can be used to perform i2c transactions to the slave devices connected to the system. There are multiple commands available, we will discuss all the commands available at the time of this writing with few examples and use cases.
Description
These days, most of the Linux systems are equipped with these commands. If any system doesn’t have these commands, these can be compiled for the system itself. Compilation for the system itself can be done only if the compiler facility is available. If the compiler is not available, then these needs to be cross compiled. Source code of these tools is open-source and compilation steps are as same as of other Linux tools.
Widely used commands available in the i2c-tools package are: i2cdetect, i2cdump, i2cget, i2cset, i2ctransfer. Let us discuss these commands in detail.
i2cdetect
This command is used to detect and list all the I2C buses available and known to the Linux.
There can be multiple I2C controllers/buses available in the system and all the buses can be listed with the i2cdetect command. Example usage of the i2cdetect is: i2cdetect -l
This command gives the below output on one system:
[ root ] $ i2cdetect -l
i2c- 1 i2c 0b234500.i2c-bus I2C adapter
i2c- 2 i2c 0b234580.i2c-bus I2C adapter
i2c- 0 i2c 0b234580.i2c-bus I2C adapter
i2c- 5 i2c 0b234500.i2c-bus I2C adapter
[ root ] $
In the output above we can see that when we execute this command with -l option it is listing all the I2C buses of the system. In the output we can see there are 4 buses available and known to the Linux. 0, 1, 2 and 5 are the busses number assigned by the Linux kernel. These are the numbers needed in other command operations.
Further information on all the slaves connected to the specific bus can also be enquired with this command. For example, if we want to get the details on bus no 0, we can issue command as i2cget -y 0.
Output of the command on our system is:
[ root ] $ i2cdetect -y 0
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: — — — — — — — — — — — — —
10 : — — — — — — — — — — — — — — — —
20 : — — — — — — — — — — — — — — — —
30 : 30 — — — — — 36 — — — — — — — — —
40 : — — — — — — — — — — — — — — — —
50 : 50 — 52 — — — — — — — — — — — — —
60 : — — — — — — — — — — — — — — — —
70 : — — — — — — — —
[ root ] $
As we can see in the logs above, there are 4 slaves on bus 0. Slave address of those I2C slave devices on bus 0 are 0x30, 0x36, 0x50, 0x52. This I2C slave address is also needed for i2cget, i2cget, i2cdump commands.
i2cget
i2cget can be used to read the I2C slave device. Any internal readable address can be read with the i2cget command. Sample usage of this command can be demonstrated with an instance, say we want to read the offset/internal address as 0x0 of I2C slave device with slave address(0x50) on bus no 0. Logs of the operation from the device is:
In the output logs. we can see the data at offset 0 is 0x23. In similar way, this command can be used to read any slave device on any I2C bus or any internal address of the I2C slave device.
i2cset
i2cget command can be used to write the data at any specified internal address of the I2C slave device. I2C internal device address should be writable. I2C write operation can be protected at the device level or any internal address can be write-only. With all the writable permissions, i2cset command can update the device.
Example usage of the command, let us take an example of writing a data value 0x12 to RTC slave device with slave address 0x68 at offset 0x2. We will demonstrate the write operation in the following sequence:
- Read the device at offset 0x2
- Write the 0x12 at offset 0x2 of slave device 0x68
- Read back the device at offset 0x2 and verify the data should be 0x12.
1 .Read the device at offset 0x2.
[ root ] $ i2cget -y 1 0x68 0x2
0x14
[ root ] $
2 .Write the 0x12 at offset 0x2 of slave device 0x68
[ root ] $ i2cset -y 1 0x68 0x2 0x12
[ root ] $
3 .Read back the device at offset 0x2 and verify the data should be 0x12.
[ root ] $ i2cget -y 1 0x68 0x2
0x12
[ root ] $
Above example steps/output in the box demonstrates the write operation on the I2C slave device. Similar steps can be followed to write any data to the I2C slave device. Slave address, data or bus number can be changed as per the system and need.
i2cdump
i2cdump command can be used to dump data from any I2C slave device. Only input needed for this command execution is the I2C bus number, slave address. Range of address can also be specified with the command. Let us take an example of reading bytes from offset 0x0 to 0xF i.e., first 16 bytes.
[ root ] $ i2cdump -y -r 0x0-0xf 1 0x68
No size specified ( using byte-data access )
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 58 29 12 06 08 10 21 00 00 00 00 00 00 00 18 00 X ) . ! .
[ root ] $
Range address is optional, if this range is not specified by default, it dumps first 0xFF bytes. i.e., 256 bytes.
i2ctransfer
i2ctransfer command is very useful and can be used to read or write multiple number of bytes in the same command.
i2ctransfer to read 14 bytes from 0ffset 0x2, command will be as follows:
[ root ] $ i2ctransfer -y 1 w1 @ 0x68 2 r14
0x12 0x06 0x08 0x10 0x21 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x18 0x00
[ root ] $
i2ctransfer to write 2 bytes data 0x10, 0x16 at offset 0x1 and 0x2, command will be as follows:
[ root ] $ i2ctransfer -y 1 w3 @ 0x68 1 0x10 0x16
[ root ] $
Readback; to confirm the write data:
[ root ] $ i2ctransfer -y 1 w1 @ 0x68 1 r2
0x10 0x16
[ root ] $
Above examples demonstrated the i2ctransfer usage with an instance. With the help of these usage, another use cases can be easily performed. Any slave device and any internal address can be read with the help of this command.
What if the Slave Device is 2-byte Addressable?
There are few I2C slave devices, specifically EEPROM device which are 2 byte-addressable. I2C transfer provides the easier way to access the device in such scenario. If this device, we want to access with i2cget/i2cset we have to consider the 2 bytes addressing.
I have an EEPROM device with me which is 2-byte addressable. Let us observe the i2cget/i2cset with EEPROM and then we will observe the i2ctransfer:
We will try to read byte from offset 0. We will try with the same command as discussed in the previous section of i2cget i.e., command will be: i2cget -y 1 0x50 0
We can see the data returned is 0xff, hence this is not the correct data.
To successfully read from offset 0, we have to first write 2-byte address with i2cset command. This is the way to read the data from 2 byte-addressable device. Example use-case:
In the i2cset command we have to write the 2-byte internal EEPROM address. Two 0’s after slave address 0x50 are the internal EEPROM address as 0x0000.
After that if we read the data with i2cget, we will get the correct data. We can see in our example it is 0x45. Previously it was 0xFF, which is an invalid data.
i2ctransfer in 2-byte Addressing Device
i2ctransfer can provide the data with the same command. Consider same example use case as of i2cget/i2cset as above.
With this command, we can read the data at offset 0000. Note that we must write internal address after splitting into 2 bytes.
Another example, reading 16 bytes from offset 0x0000:
[ root ] $ i2ctransfer -y 1 w2 @ 0x50 0x0 0x0 r16
0x45 0x41 0x3d 0x41 0x41 0x42 0x42 0x43 0x43 0x44 0x44 0x44 0x45 0x45 0x30 0x0a
[ root ] $
One more example to read 4 bytes from offset 0x0004:
This example can be verified with the previous read operation where we have read 16 bytes from offset 0000. Now, we have read the subset. If we compare the results of this read operation and verify with the previous one, results exactly match. Hence, we can conclude that this read is successful.
Conclusion
We have discussed the I2C tool package in Linux. Various commands are available in this i2c-tools package. Some special use cases like 2-bytes addressing, how to use commands in these special scenarios. Many example we have seen so far. We confirmed all the commands functioning with the example and demonstrations. I2cset, i2cget, i2cdump, i2cdetect and i2ctransfer are the commands of the I2C -tools package.
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.