Android I2C 驱动

allwinner rockchip

Posted by LXG on April 10, 2023

I2C通信协议:了解I2C Primer、 PMBus和SMBus

I2C概念

I2C,即Inter-Integrated Circuit,是一种常用的串行通信协议,用于在器件之间——特别是两个或两个以上不同电路之间建立通信。

I2C 有利于设计人员在系统的众多节点之间建立简单、双向、灵活的通信。I2C仅使用两条双向线来发送和接收信息,从而降低了复杂性。它还允许设计人员配置多个主节点系统IC之间的通信。

根据定义,Inter-Integrated Circuit (I2C)——也称为Inter IC——是一种硬件通信协议,它通过一条多主器件、多节点、串行通信总线进行同步通信。同步通信意味着两个(或两个以上)交换数据的器件共享一条公共时钟线。I2C广泛用于将低速外围IC连接到处理器和微控制器。I2C总线由飞利浦公司设计,它让位于同一电路板上的器件之间可以轻松通信。

接口

使用一条串行数据(SDA)线、一条串行时钟(SCL)线和一个公共接地来承载所有通信,最大程度地减少连接。

i2c_interface

每个I2C器件有两条线路:

  • SDA是供主器件和节点发送和接收数据的线路。
  • SCL是承载时钟信号的线路。SCL总是由I2C主器件生成。规范对时钟信号的低相位和高相位有最短周期要求。

I2C总线仅使用两条双向线路:每个器件的SDA和SCL用于简单的IC间通信。

i2c_interface_2

硬件最重要的注意是在SDA和SCL上加入上拉电阻。I2C器件通过开集或开漏引脚连接到总线,将线路拉低。当没有数据传输时,I2C总线处于高电平空闲状态;线路被被动拉高。要传输数据,须切换线路,即先拉低再释放(又变为高电平)。数据位在时钟下降沿传输。

开漏输出需要一个上拉电阻(图2中的Rp)才能正确输出高电平。上拉电阻连接在输出引脚和高电平所需的输出电压(图2中的VDD)之间。

对于VCC和VDD (5 V)的典型值,4700 Ω是最常用的上拉电阻值。

I2C链路的最大总线长度约为1米(100 kBaud时)或10米(10 kBaud时)。非屏蔽电缆的电容通常要小得多,但只能用在以其他方式加以屏蔽的外壳内。

A133 案例

a133_i2c

总结

i2c_characteristic

I2C 是同步的,因此位的输出通过主器件和节点之间共享的时钟信号与位的采样同步。时钟信号始终由主器件控制。

I2C读写协议

i2c_interface_3

全志平台

Android 10 TWI模块使用说明书.pdf

A100 平台支持 6 路 TWI,包含 4 路 TWI 与 2 路 S_TWI,其中 S_TWI0 作为与 PMU 通信使用,该 TWI 不建议复用为其他功能。

TWI: Two Wire Interface,全志平台兼容 I2C 标准协议的总线控制器

驱动源码-A133


kernel/linux­4.9/drivers/i2c/
├── busses
 ├── i2c­sunxi.c      // Sunxi平台的I2C控制器驱动代码
 ├── i2c­sunxi.h      // 为Sunxi平台的I2C控制器驱动定义了一些宏、数据结构
 ├── i2c­sunxi­test.c  // Sunxi平台的i2c设备测试代码
├── i2c­core.c         // I2C子系统核心文件,提供相关的接口函数
├── i2c­dev.c          // I2C子系统的设备相关文件,用以注册相关的设备文件,方便调试

架构

a133_i2c_arch

DTS

sun50iw10p1.dtsi


/ {
	model = "sun50iw10";
	compatible = "arm,sun50iw10p1";

	aliases {
		twi0 = &twi0;
		twi1 = &twi1;
		twi2 = &twi2;
		twi3 = &twi3;
		twi4 = &twi4;
		twi5 = &twi5;
		twi6 = &twi6;
		twi7 = &twi7;
	}

	soc: soc@03000000 {
		compatible = "simple-bus";
		#address-cells = <2>;
		#size-cells = <2>;
		ranges;
		device_type = "soc";

		twi0: twi@0x05002000{
			#address-cells = <1>;
			#size-cells = <0>;
			compatible = "allwinner,sun50i-twi";          // 具体的设备,用于驱动和设备的绑定
			device_type = "twi0";                         // 设备节点名称,用于sys_config.fex匹配
			reg = <0x0 0x05002000 0x0 0x400>;             // TWI0总线寄存器配置
			interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>; // TWI0总线中断号、中断类型
			clocks = <&clk_twi0>;                         // 设备使用的时钟
			clock-frequency = <400000>;                   // TWI0控制器的时钟频率
			pinctrl-names = "default", "sleep";           // TWI0控制器使用的Pin脚名称,其中default为正常通信时的引脚配置,sleep为睡眠时的引脚配置
			pinctrl-0 = <&twi0_pins_a>;                   // TWI0控制器default时使用的pin脚配置
			pinctrl-1 = <&twi0_pins_b>;                   // TWI0控制器sleep时使用的pin脚配置
			status = "disabled";
		};

        }

}

board.dts


#include "sun50iw10p1.dtsi"
/{
	model = "sun50iw10";
	compatible = "allwinner,a133", "arm,sun50iw10p1";
	soc@03000000 {

		pio: pinctrl@0300b000 {

			twi0_pins_a: twi0@0 {
				allwinner,pins = "PH0", "PH1";
				allwinner,pname = "twi0_scl", "twi0_sda";
				allwinner,function = "twi0";
				allwinner,muxsel = <2>;
				allwinner,drive = <1>;
				allwinner,pull = <0>;
			};

			twi0_pins_b: twi0@1 {
				allwinner,pins = "PH0", "PH1";
				allwinner,function = "io_disabled";
				allwinner,muxsel = <7>;
				allwinner,drive = <1>;
				allwinner,pull = <0>;
			};

			twi1_pins_a: twi1@0 {
				allwinner,pins = "PB4", "PB5";
				allwinner,pname = "twi1_scl", "twi1_sda";
				allwinner,function = "twi1";
				allwinner,muxsel = <2>;
				allwinner,drive = <1>;
				allwinner,pull = <1>;
			};

			twi1_pins_b: twi1@1 {
				allwinner,pins = "PB4", "PB5";
				allwinner,function = "io_disabled";
				allwinner,muxsel = <7>;
				allwinner,drive = <1>;
				allwinner,pull = <0>;
			};

			twi2_pins_a: twi2@0 {
				allwinner,pins = "PE1", "PE2";
				allwinner,pname = "twi2_scl", "twi2_sda";
				allwinner,function = "twi2";
				allwinner,muxsel = <2>;
				allwinner,drive = <1>;
				allwinner,pull = <0>;
			};

			twi2_pins_b: twi2@1 {
				allwinner,pins = "PE1", "PE2";
				allwinner,function = "io_disabled";
				allwinner,muxsel = <7>;
				allwinner,drive = <1>;
				allwinner,pull = <0>;
			};

			twi3_pins_a: twi3@0 {
				allwinner,pins = "PE3", "PE4";
				allwinner,pname = "twi3_scl", "twi3_sda";
				allwinner,function = "twi3";
				allwinner,muxsel = <2>;
				allwinner,drive = <1>;
				allwinner,pull = <1>;
			};

			twi3_pins_b: twi3@1 {
				allwinner,pins = "PE3", "PE4";
				allwinner,function = "io_disabled";
				allwinner,muxsel = <7>;
				allwinner,drive = <1>;
				allwinner,pull = <0>;
			};
		}

		twi0: twi@0x05002000{
			clock-frequency = <400000>;
			pinctrl-0 = <&twi0_pins_a>;
			pinctrl-1 = <&twi0_pins_b>;
			twi-supply = <&reg_ldoio0>;
			status = "disable";
			ctp {
				compatible = "allwinner,gslX680";
				reg = <0x5d>;
				device_type = "ctp";
				status = "disabled";
				ctp_name = "gslX680_3676_1280x800";
				ctp_twi_id = <0x0>;
				ctp_twi_addr = <0x5d>;
				ctp_screen_max_x = <0x320>;
				ctp_screen_max_y = <0x500>;
				ctp_revert_x_flag = <1>;
				ctp_revert_y_flag = <1>;

				ctp_exchange_x_y_flag = <0x1>;
				ctp_int_port = <&pio PH 9 6 0xffffffff 0xffffffff 0>;
				ctp_wakeup = <&pio PH 10 1 0xffffffff 0xffffffff 1>;
				ctp-supply = <&reg_ldoio0>;
				ctp_power_ldo = <&reg_ldoio0>;
				ctp_power_ldo_vol = <3300>;
			};
		};

		twi1: twi@0x05002400{
			clock-frequency = <200000>;
			pinctrl-0 = <&twi1_pins_a>;
			pinctrl-1 = <&twi1_pins_b>;
			status = "okay";
			ctp {
				compatible = "allwinner,goodix";
				reg = <0x5d>;
				device_type = "ctp";
				status = "okay";
				ctp_name = "gt9xxnew_ts";
				ctp_twi_id = <0x1>;
				ctp_twi_addr = <0x5d>;
				ctp_screen_max_x = <0x780>; /* 1920 */
				ctp_screen_max_y = <0x438>; /* 1080 */
				ctp_revert_x_flag = <0>;
				ctp_revert_y_flag = <0>;

				ctp_exchange_x_y_flag = <0x0>;
				ctp_int_port = <&pio PH 18 6 0xffffffff 0xffffffff 0>;
				ctp_wakeup = <&pio PH 16 1 0xffffffff 0xffffffff 1>;
				ctp-supply = <&reg_ldoio0>;
				ctp_power_ldo = <&reg_ldoio0>;
				ctp_power_ldo_vol = <3300>;
			};
		};

		twi2: twi@0x05002800{
			clock-frequency = <200000>;
			pinctrl-0 = <&twi2_pins_a>;
			pinctrl-1 = <&twi2_pins_b>;
			twi-supply = <&reg_dldo2>;
			status = "okay";
		};

		twi3: twi@0x05002c00{
			clock-frequency = <200000>;
			pinctrl-0 = <&twi3_pins_a>;
			pinctrl-1 = <&twi3_pins_b>;
			status = "okay";
			24c02@50 {
				compatible = "Atmel,24c02";
				reg = <0x50>;
				status = "okay";
			};
		};
	}

}

节点查看


ceres-c3:/ $ ls -al sys/bus/i2c/
total 0
drwxr-xr-x  4 root root    0 1970-01-01 08:00 .
drwxr-xr-x 23 root root    0 1970-01-01 08:00 ..
drwxr-xr-x  2 root root    0 1970-01-01 08:00 devices
drwxr-xr-x 12 root root    0 1970-01-01 08:00 drivers
-rw-r--r--  1 root root 4096 2023-04-11 09:56 drivers_autoprobe
--w-------  1 root root 4096 2023-04-11 09:56 drivers_probe
--w-------  1 root root 4096 2023-04-11 09:56 uevent


ceres-c3:/ $ ls -al sys/bus/i2c/drivers
total 0
drwxr-xr-x 12 root root 0 1970-01-01 08:00 .
drwxr-xr-x  4 root root 0 1970-01-01 08:00 ..
drwxr-xr-x  2 root root 0 2023-04-11 10:09 ac107
drwxr-xr-x  2 root root 0 2023-04-11 09:56 at24                  // eeprom 驱动
drwxr-xr-x  2 root root 0 2023-04-11 10:09 axp20x-i2c            // 电源管理驱动
drwxr-xr-x  2 root root 0 2023-04-11 10:09 dummy
drwxr-xr-x  2 root root 0 1970-01-01 08:00 gc2053_mipi           // 相机
drwxr-xr-x  2 root root 0 1970-01-01 08:00 gc2053_mipi_2
drwxr-xr-x  2 root root 0 2023-04-10 17:52 gt9xxnew_ts           // 触摸
drwxr-xr-x  2 root root 0 2023-04-11 10:09 ir-kbd-i2c
drwxr-xr-x  2 root root 0 2023-04-11 10:09 sc7a20
drwxr-xr-x  2 root root 0 2023-04-11 10:09 stk3x1x

ceres-c3:/ $ ls -al  sys/bus/i2c/devices/
total 0
drwxr-xr-x 2 root root 0 1970-01-01 08:00 .
drwxr-xr-x 4 root root 0 1970-01-01 08:00 ..
lrwxrwxrwx 1 root root 0 2023-04-11 09:56 1-005d -> ../../../devices/platform/soc/twi1/i2c-1/1-005d
lrwxrwxrwx 1 root root 0 1970-01-01 08:00 3-0050 -> ../../../devices/platform/soc/twi3/i2c-3/3-0050
lrwxrwxrwx 1 root root 0 2023-04-11 09:56 6-0034 -> ../../../devices/platform/soc/7081400.s_twi/i2c-6/6-0034
lrwxrwxrwx 1 root root 0 2023-04-11 09:56 i2c-1 -> ../../../devices/platform/soc/twi1/i2c-1
lrwxrwxrwx 1 root root 0 2023-04-11 09:56 i2c-2 -> ../../../devices/platform/soc/twi2/i2c-2
lrwxrwxrwx 1 root root 0 2023-04-11 09:56 i2c-3 -> ../../../devices/platform/soc/twi3/i2c-3
lrwxrwxrwx 1 root root 0 2023-04-11 09:56 i2c-6 -> ../../../devices/platform/soc/7081400.s_twi/i2c-6

当前 TWI 通道的一些硬件资源信息


ceres-c3:/ $ cat sys/devices/platform/soc/twi1/info
pdev->id   = 1
pdev->name = twi1
pdev->num_resources = 2
pdev->resource.mem = [0x0000000005002400, 0x00000000050027ff]
pdev->resource.irq = 0x0000000000000162
pdev->dev.platform_data.bus_num  = 1
pdev->dev.platform_data.freqency = 200000
pdev->dev.platform_data.regulator= 0xffffffc03daa0d00
pdev->dev.platform_data.regulator_id =

当前 TWI 通道的一些运行状态信息,包括控制器的各寄存器值


ceres-c3:/ $ cat sys/devices/platform/soc/twi1/status
i2c->bus_num = 1
i2c->status  = [1] Idle
i2c->msg_num   = 0, ->msg_idx = 1, ->msg_ptr = 0
i2c->bus_freq  = 200000
i2c->irq       = 354
i2c->debug_state = 40
i2c->base_addr = 0xffffff800807c400, the TWI control register:
[ADDR] 0x00 = 0x00000000, [XADDR] 0x04 = 0x00000000
[DATA] 0x08 = 0x00000001, [CNTR] 0x0c = 0x00000040
[STAT]  0x10 = 0x000000f8, [CCR]  0x14 = 0x000000d8
[SRST] 0x18 = 0x00000000, [EFR]   0x1c = 0x00000000
[LCR]  0x20 = 0x0000003a

i2c­tools 调试工具

获取i2c设备信息


ceres-c3:/ $ i2cdetect -l
i2c-3	i2c       	twi3                            	I2C Adapter         // eeprom
i2c-1	i2c       	twi1                            	I2C Adapter         // 触摸
i2c-6	i2c       	twi6                            	I2C Adapter
i2c-2	i2c       	twi2                            	I2C Adapter         // camera

获取I2C设备寄存器信息


ceres-c3:/ $ i2cdump -f 3 0x50
Dump chip 0x50 on bus 3? (Y/n):y
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
00: 41 36 52 36 32 38 30 30 30 30 32 31 31 31 39 57    A6R628000021119W
10: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ????????????????
20: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ????????????????
30: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ????????????????
40: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ????????????????
50: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ????????????????
60: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ????????????????
70: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ????????????????
80: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ????????????????
90: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ????????????????
a0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ????????????????
b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ????????????????
c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ????????????????
d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ????????????????
e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ????????????????
f0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ????????????????

常见问题分析

Android 10 TWI模块使用说明书.pdf

TWI数据未完全发送


sunxi_i2c_do_xfer()1946 - [i2c1] incomplete xfer (status: 0x20, dev addr: 0x5d)
sunxi_i2c_do_xfer()1946 - [i2c1] incomplete xfer (status: 0x48, dev addr: 0x5d)

问题分析:此错误表示主控已经发送了数据(status 值为 0x20 时,表示发送了 SLAVE ADDR +WRITE;status 值为 0x48 时,表示发送了 SLAVE ADDR + READ),但是设备没有回 ACK,这表明设备无响应,应该检查是否未接设备、接触不良、设备损坏和上电时序不正确导致的设备未就绪等问题。

排查步骤:

  1. 通过设备树里面的配置信息,核对引脚配置是否正确。每组 TWI 都有好几组引脚配置
  2. 更换 TWI 总线下的设备为 at24c16,用 i2ctools 读写 at24c16 看看是否成功,成功则表明总线工作正常;
  3. 排查设备是否可以正常工作以及设备与 I2C 之间的硬件接口是否完好;
  4. 详细了解当前需要操作的设备的初始化方法,工作时序,使用方法,排查因初始化设备不正确导致通讯失败;
  5. 用示波器检查 TWI 引脚输出波形,查看波形是否匹配。

TWI 起始信号无法发送


sunxi_i2c_do_xfer()1865 ­ [i2c1] START can't sendout!

问题分析:此错误表示 TWI 无法发送起始信号,一般跟 TWI 总线的引脚配置以及时钟配置有关。应该检查引脚配置是否正确,时钟配置是否正确,引脚是否存在上拉电阻等等。

排查步骤:

  1. 重新启动内核,通过查看 log,分析 TWI 是否成功初始化,如若存在引脚配置问题,应核对引脚信息是否正确
  2. 根据原理图,查看 TWI­SCK 和 TWI­SDA 是否经过合适的上拉电阻接到 3.3v 电压;
  3. 用万用表量 SDA 与 SCL 初始电压,看电压是否在 3.3V 附近(断开此 TWI 控制器所有外设硬件连接与软件通讯进程);
  4. 核查引脚配置以及 clk 配置是否进行正确设置;
  5. 测试 PIN 的功能是否正常,利用寄存器读写的方式,将 PIN 功能直接设为 INPUT 功能(echo [reg] [val] > /sys/class/sunxi_dump/write),然后将 PIN 上拉和接地改变 PIN 状态,读 PIN 的状态 (echo [reg,reg] > /sys/class/sunxi_dump/dump;cat dump),看是否匹配。
  6. 测试 CLK 的功能是否正常,利用寄存器读写的方式,将 TWI 的 CLK gating 等打开,(echo[reg] [val] > /sys/class/sunxi_dump/write),然后读取相应 TWI 的寄存器信息,读 TWI 寄存器的数据(echo [reg] ,[len]> /sys/class/sunxi_dump/dump),查看寄存器数据是否正常

TWI 终止信号无法发送


twi_stop()511 ­ [i2c4] STOP can't sendout!
sunxi_i2c_core_process()1726 ­ [i2c4] STOP failed!

问题分析和排查步骤如上

TWI 传送超时


[123.681219] sunxi_i2c_do_xfer()1914 ­ [i2c3] xfer timeout (dev addr:0x50)

问题分析:此错误表示主控已经发送完起始信号,但是在与设备通信的过程中无法正常完成数据发送与接收,导致最终没有发出终止信号来结束 I2C 传输,导致的传输超时问题。应该检查引脚配置是否正常,CLK 配置是否正常,TWI 寄存器数据是否正常,是否有其他设备干扰,中断是否正常等问题。

排查步骤:

  1. 核实 TWI 控制器配置是否正确
  2. 根据原理图,查看 TWI­SCK 和 TWI­SDA 是否经过合适的上拉电阻接到 3.3v 电压;
  3. 用万用表量 SDA 与 SCL 初始电压,看电压是否在 3.3V 附近(断开此 TWI 控制器所有外设硬件连接与软件通讯进程);
  4. 关闭其他 TWI 设备,重新进行烧录测试 TWI 功能是否正常;
  5. 测试 PIN 的功能是否正常,利用寄存器读写的方式,将 PIN 功能直接设为 INPUT 功能(echo [reg] [val] > /sys/class/sunxi_dump/write),然后将 PIN 上拉和接地改变 PIN 状态,读 PIN 的状态 (echo [reg,reg] > /sys/class/sunxi_dump/dump;cat dump),看是否匹配。
  6. 测试 CLK 的功能是否正常,利用寄存器读写的方式,将 TWI 的 CLK gating 等打开,(echo[reg] [val] > /sys/class/sunxi_dump/write),然后读取相应 TWI 的寄存器信息,读 TWI 寄存器的数据(echo [reg] ,[len]> /sys/class/sunxi_dump/dump),查看寄存器数据是否正常