Accessing I2C Devices in Linux Print


The Linux kernel provides a device driver for the I2C controller of the STM32F7, enabled in the kernel with the CONFIG_I2C_STM32F7 build-time option. Another kernel configuration option that you will require is CONFIG_I2C_CHARDEV. This option enables the kernel API that allows accessing I2C devices from user-space application code. Both these kernel configuration options are enabled by default in the rootfs project.

The STM32F7 includes four I2C bus controllers. The parameters of each bus controller (interrupt request numbers, DMA channels, etc.) are specified in the i2c_x child nodes properties in the soc node in the arch/arm/boot/dts/stm32.dtsi and arch/arm/boot/dts/stm32f7.dtsi files:

soc {
...
i2c_1: i2c@40005400 {
compatible = "st,stm32-i2c", "st,stm32f7-i2c";
reg = <0x40005400 0x400>;
interrupts = <31 32>;
clocks = <&rcc 0 149>;
resets = <&rst STM32F4_APB1_RESET(I2C1)>;
dmas = <&dma1 5 1>, <&dma1 6 1>;
dma-names = "rx", "tx";
status = "disabled";
};
...
};

The bus number asignment per I2C controller is made in these *.dtsi files too, in the aliases node:

aliases {
...
i2c0 = &i2c_1;
i2c1 = &i2c_2;
...
};

The above two dtsi files are generic for the STM family and the STM32F7 processor so you won't have to modify them in order to configure I2C for application. The above information is provided for your reference.

The STM32F7 allows different alternate pin selection for each I2C controller. Assignment of the I2C signals to specific pins is described in the pinctrl_i2c_x child nodes of the pin-controller node in the arch/arm/boot/dts/stm32-som.dtsi and arch/arm/boot/dts/stm32f7-som.dtsi files:

pin-controller {
...
i2c_1 {
pinctrl_i2c_1: i2c_1-0 {
st,pins {
scl = <&gpiob 8 ALT4
PULL_UP OPEN_DRAIN LOW_SPEED>;
sda = <&gpiob 7 ALT4
PULL_UP OPEN_DRAIN LOW_SPEED>;
};
};
};
...
};

The above dtsi file is specific to the Emcraft STM32F7 SOM. You will have to update it only in case you decide to route the I2C interfaces to alternate pads of the STM32F7. Use caution when updating this file to avoid conflict in allocation of various I/O interfaces to STM32F7 pads.

Finally the rootfs.dts.STM32F7 file defines the I2C interface reference, with additional properties which enable the appropriate I2C bus controller, connect it to the pins specified, and select the bus speed:

&i2c_1 {
status = "okay";
st,i2c-clk = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c_1>;

#address-cells = <1>;
#size-cells = <0>;
};

The Emcraft uClinux distribution includes the Linux run-time tools that can be used to access I2C devices from user space. The following lines in the rootfs.initramfs file add these tools to the target file system:

file /usr/sbin/i2cdump ${INSTALL_ROOT}/A2F/root/usr/sbin/i2cdump 755 0 0
file /usr/sbin/i2cdetect ${INSTALL_ROOT}/A2F/root/usr/sbin/i2cdetect 755 0 0
file /usr/sbin/i2cset ${INSTALL_ROOT}/A2F/root/usr/sbin/i2cset 755 0 0
file /usr/sbin/i2cget ${INSTALL_ROOT}/A2F/root/usr/sbin/i2cget 755 0 0

The functionality described above is enabled by default in the rootfs project. So, just run the rootfs.uImage binary on your target and observe the kernel messages about the I2C bus being enabled:

i2c /dev entries driver
stm32-pinctrl pin-controller: maps: function i2c_1 group i2c_1-0 num 3
stm32f7-i2c 40005400.i2c: I2C Controller i2c-0 at 40005400,irq=19

Run the Linux I2C tools to examine I2C devices on your target. For instance, the following command scans the I2C0 interface and reports any devices it detects on the bus:

/ # i2cdetect -y 0
0  1  2  3  4  5  6  7  8  9  a   b  c  d  e  f
00:-- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- 56 -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

Refer to the documentation available from the Internet for detailed manuals on the Linux I2C tools, for instance: http://www.lm-sensors.org/wiki/i2cToolsDocumentation.

Sometimes your I2C device is already supported in the Linux kernel, and you want to utilize it in some standard way. To provide such access to the I2C device you need:

  • Enable the appropriate I2C device driver in your Linux kernel configuration;
  • Add information about your I2C device into the appropriate i2c node reference in the rootfs.dts.STM32F7 file.

Let's for example connect an AT24 EEPROM with address 0x56 to the I2C0 bus, and provide user with a simple read/write interface to it:

  • Enable the EEPROM driver in the Linux kernel configuration (Device Drivers -> Misc devices -> EEPROM support -> I2C EEPROMs / RAMs / ROMs from most vendors):
  • [yur@ubuntu ~/projects/rootfs] $ make kmenuconfig

  • Add information about the AT24 EEPROM device into rootfs.dts.STM32F7:
  • &i2c_1 {
    ...
    eeprom0: eeprom0@56 {
    compatible = "at,24c256";
    reg = <0x56>;
    };
    };

  • Build rootfs.uImage:
  • [yur@ubuntu ~/projects/rootfs] $ make

  • Run the resultant image on the target, and validate access to the EEPROM device via the standard interfaces defined by the kernel drivers:
  • Starting kernel ...
    ...
    i2c /dev entries driver
    stm32-pinctrl pin-controller: maps: function i2c_1 group i2c_1-0 num 3
    at24 0-0056: 32768 byte 24c256 EEPROM, writable, 1 bytes/write
    stm32f7-i2c 40005400.i2c: I2C Controller i2c-0 at 40005400,irq=19
    ...
    init started: BusyBox v1.17.0 (2016-09-20 14:00:02 +0400)
    / # echo -e "Some text\n" > /sys/bus/i2c/devices/0-0056/eeprom
    / # cat /sys/bus/i2c/devices/0-0056/eeprom
    Some text written to EEPROM