20220916imx6ull上使用linux的i2c gpio驱动

IMX6ULL Linux 4.1.15

使用IMX6ULL时发现,一旦硬件I2C受到干扰,I2C驱动就会陷入卡死的状态,表现为ioctl操作一直返回22(-EAGAIN,resource temporarily unavailable),无法恢复,比如我现在调试的板子使用了FM接收芯片,一旦FM发射源离得很近,I2C总线就会死掉

虽然干扰的路径未知,但总不希望软件这么脆弱,所以尝试开启GPIO实现的I2C总线,大致思路:

  • 开启GPIO实现的I2C驱动CONFIG_I2C_GPIO=y,并配置设备树
  • 设备树中配置pinmux,配置对应的IO复用功能为GPIO
  • 用户态i2c接口与Linux Kernel下文档Documentation/i2c/dev-interface有关,内核配置开启CONFIG_I2C_CHARDEV

i2c-gpio.c驱动,对应binding文档Documentation/devicetree/bindings/i2c/i2c-gpio.txt

Device-Tree bindings for i2c gpio driver

Required properties:
    - compatible = "i2c-gpio";
    - gpios: sda and scl gpio


Optional properties:
    - i2c-gpio,sda-open-drain: sda as open drain
    - i2c-gpio,scl-open-drain: scl as open drain
    - i2c-gpio,scl-output-only: scl as output only
    - i2c-gpio,delay-us: delay between GPIO operations (may depend on each platform)
    - i2c-gpio,timeout-ms: timeout to get data

Example nodes:

i2c@0 {
    compatible = "i2c-gpio";
    gpios = <&pioA 23 0 /* sda */
         &pioA 24 0 /* scl */
        >;
    i2c-gpio,sda-open-drain;
    i2c-gpio,scl-open-drain;
    i2c-gpio,delay-us = <2>;    /* ~100 kHz */
    #address-cells = <1>;
    #size-cells = <0>;

    rv3029c2@56 {
        compatible = "rv3029c2";
        reg = <0x56>;
    };
};

开启内核配置CONFIG_I2C_GPIO=y,模仿文档修改设备树,dts中添加:

    i2c1_gpio {
        #address-cells = <1>;
        #size-cells = <0>;
        compatible = "i2c-gpio";
        gpios = <
            &gpio1 29 GPIO_ACTIVE_HIGH /* SDA */
            &gpio1 28 GPIO_ACTIVE_HIGH /* SCL */
        >;
        i2c-gpio,delay-us = <5>;    /* ~100 kHz */
        status = "okay";
    };

添加IO复用配置:

        pinctrl_hog_1: hoggrp-1 {
            fsl,pins = <
                ...

                /*
                    模块pin64, GPIO1_28, I2C1_SCL
                    模块pin65, GPIO1_29, I2C1_SDA
                */
                MX6UL_PAD_UART4_TX_DATA__GPIO1_IO28 0x1b0b1
                MX6UL_PAD_UART4_RX_DATA__GPIO1_IO29 0x1b0b1
            >;
        };

重新烧上内核和DTB:

root@ebsx-imx6ull:~# dmesg |grep i2c
i2c-gpio i2c1: using pins 29 (SDA) and 28 (SCL)
root@ebsx-imx6ull:~# ls /dev | grep i2c
i2c-1
i2c-3
i2c-4
root@ebsx-imx6ull:~# i2cdetect -l
i2c-1   i2c         21a4000.i2c                         I2C adapter
i2c-3   i2c         21f8000.i2c                         I2C adapter
i2c-4   i2c         i2c1                                I2C adapter
root@ebsx-imx6ull:~# i2cdetect -y 4
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: 10 11 -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: 60 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

一切正常,打完收工!