I2C Framework

Linux I2C Framework

Linux kernel: 3.10.67

I2C Protocol: UM10204 I2C-bus specification and user manual

I2C Bus Protocol

I2C(Inter-Integrated Circuit)是 Philips 推出的适用于近距离低速芯片间通信的主从式的串行总线标准,支持多个主节点和多个从节点

I2C 总线是主从式总线,主设备(master)负责产生时钟信号、决定一次数据传输的开始与结束,而其它被主设备寻址的均为从设备(slave)

Bus Specification

信号

I2C-c600

总线由双向串行数据信号 SDA 和双向串行时钟信号 SCL 构成,主节点和从节点分别并联至总线。

时序

I2C_bit_transfer-c400
数据传输时,SCL 高电平期间 SDA 须保持稳定,SCL 低电平期间 SDA 可以改变数据。

I2C_start_stop-c500
数据传输在开始信号S和结束信号P之间进行,SCL高电平期间SDA由高电平变为低电平表示开始信号S,SDA由低电平变为高电平表示结束信号P;master device发送开始信号后认为总线处于busy状态,直至master device发送结束信号并等待一段时间后认为总线恢复free状态

I2c_byte_transfer-c

一个字节的数据传输需要9个时钟周期,其中前8位为传输的一字节的数据,第9位为receiver的应答信号。传输完一个字节的数据后,第9个时钟低电平期间,transmitter释放 SDA,receiver将 SDA 拉低,以发送应答信号;第9个时钟高电平期间,transmitter收到应答信号,发起下一个字节数据的传输或发送结束信号。

当从设备需要响应中断事件而不能及时接收或发送I2C数据时,可以拉低SCL,并迫使主设备进入等待状态,直到从设备拉高SCL,I2C总线重新恢复为正常的数据传输状态

-c600

开始信号后的第一个字节用来传输从节点的地址,前7位为地址码,第8位为读写位(R/W)。

START Slave Address R/nW ACK Data ACK Data ACK STOP
1 bit 7 bit 1 bit 1 bit 8 bit 1 bit 8 bit 1 bit 1 bit

I2C_data_transfe-c

  1. SCL 高电平时,SDA 由高电平向低电平跳变,数据传输开始(S)
  2. SCL 低电平时,SDA 设置为第一位数据的电平
  3. SCL 高电平时,对 SDA 进行采样获得第一位数据(B1)
  4. 重复过程2、3
  5. SCL 高电平时,SDA 由低电平向高电平跳变,数据传输结束(P)

一次数据传输在开始信号S和结束信号P之间进行,其间可以进行多个字节的数据传输,字节的数量不受限制;一个字节的数据传输需要9个时钟周期,其中前8位为传输的一字节的数据,第9位为从节点的应答信号ACK或NACK, 字节数据的传输遵从Most Significant Bit (MSB) first原则;同时当进行连续的多次数据传输时,其间的结束信号P可以省略,而是直接发送开始信号S以开始新的一次数据传输

在第9个时钟低电平期间,transmitter释放 SDA,receiver将 SDA 拉低,并在第9个时钟高电平期间使其保持低电平状态,以发送应答信号ACK,该信号用于通知transmitter其已经成功接收之前传输的一个字节的数据,并且transmitter可以准备发送下一字节的数据

当第9个时钟高电平期间SDA保持为高电平时,receiver向transmitter发送NACK信号,此时主设备可以发送结束信号P以结束此次传输,或发送开始信号S以开始新的一次数据传输

10-bit addressing可以扩展I2C的地址空间,当使用10-bit地址时,数据传输的开始两个字节用于表示slave device的地址,其中第一个字节的前7位数据为1111 0XX,其后两个bit表示10-bit address的高2位 Most-Significant Bits (MSB),第一个字节的最后一个bit为R/nW,第二个字节表示10-bit address的低8位

I2C_10bit-c600

1
D6 D5 D4 D3 D2 D1 D0

I2C slave device的7-bit address,其中高4位由设备的生产厂商决定,与设备型号相关;其低3位固定或由外部IC引脚的电平决定

硬件拓扑

i2c_hw_topology-c550

I2C 是主从式总线,Linux 默认运行有 Linux 内核的设备为 I2C 主设备,其它的为从设备

CPU 与 slave device 的连接方式有

  • slave device 与 i2c controller 直接通过 i2c bus 相连,CPU 与设备间的数据交互与配置事务均通过i2c总线进行

  • slave device 与 i2c controller 通过 i2c bus 相连,同时slave device还通过其他总线与CPU上的其他controller相连,例如ov13850通过i2c bus与i2c controller相连,同时通过MIPI(CSI)接口与CPU连接。CPU通过i2c总线对设备的传感器进行配置,同时通过MIPI(CSI)接口与设备实现数据交互,获取传感器的输出数据

形态1

对于形态1,内核将该设备视为i2c controller下挂载的一个slave device,在设备树中通常在i2c controller的节点下描述该设备的节点信息

在i2c controller的platform driver的probe函数中为该i2c controller下的slave device分配i2c_client,并将该i2c_client注册到i2c_bus_type,当该slave device的i2c_driver注册到i2c_bus_type并匹配成功时,执行该i2c_driver的probe函数

形态2

对于形态2,内核将该设备视为一个platform device,在设备树中该设备的节点通常位于根节点之下,并且在该节点中使用一个变量,指向其I2C adapter的DTS节点,例如:”ddc-i2c-bus = <&i2c2>;”

在该设备的platform driver中以”ddc-i2c-bus”参数,调用of_parse_phandle()接口,获取I2C adapter的device node(即i2c的device node),然后调用of_find_i2c_adapter_by_node()获取相应的I2C adapter指针

Data Transfer

符号 描述
S (1 bit) 开始位
P (1 bit) 停止位
Rd/Wr (1 bit) 读写标志位,Rd为1,Wr为0
A/NA (1 bit) Accept / Reverse Accept
Addr (7 bit) slave device 的地址
Comm (8 bit) Command byte,通常包含需要读写的寄存器的地址
Data (8 bit) Data byte
Count (8 bit) 当执行块数据读写时,该字节表明数据的长度
[..] slave device 发送的数据包

对于standard I2C protocol,slave device允许不响应其i2c address,而SMBus protocol规定slave device必须响应其i2c address,通过该特性SMBus protocol可以自动探测当前插入的热插拔设备

同时对于standard I2C protocol,NA 表示receiver不能继续接收数据,从而结束该次数据传输;而对于SMBus protocol,NA 表示slave device接收到invalid command or data

Standard I2C Protocol

i2c_master_send
1
S Addr Wr [A] Data [A] Data [A] ... [A] Data [A] P

i2c_master_send(),向slave device发送count字节的数据

i2c_send-c400

i2c_master_recv
1
S Addr Rd [A] [Data] A [Data] A ... A [Data] NA P

i2c_master_recv(),从slave device接收count字节的数据

i2c_receive-c400

i2c_transfer
1
S Addr Rd [A] [Data] NA S Addr Wr [A] Data [A] P

i2c_transfer(),根据传入的多个i2c message进行多次i2c数据传输

i2c_transfer-c500

I2C_M_NOSTART
1
2
S Addr Rd [A] [Data] NA S Addr Wr [A] Data [A] P
S Addr Rd [A] [Data] NA Data [A] P

在进行读写混合操作时,当指定第二个i2c_msg的flags标志位为I2C_M_NOSTART时,实际传输时会忽略第二个message的起始信号 S Addr Rd/Wr;第一个i2c_msg的flags标志位通常不能设置为I2C_M_NOSTART时

需要i2c_algorithm的I2C_FUNC_NOSTART支持

I2C_M_IGNORE_NAK
1
S Addr Wr [A] Data [A] Data [NA] Data [A] ... [A] Data [A] P

数据传输过程中忽略从设备的NA信号,直至将所有的i2c message发送完成,需要i2c_algorithm的I2C_FUNC_PROTOCOL_MANGLING支持

I2C_M_REV_DIR_ADDR
1
S Addr Rd [A] Data [A] Data [A] ... [A] Data [A] P

将读写信号Rd/Wr翻转,即此时Rd信号表示写操作,Wr信号表示读操作,需要i2c_algorithm的I2C_FUNC_PROTOCOL_MANGLING支持,需要i2c_algorithm的I2C_FUNC_PROTOCOL_MANGLING支持

I2C_M_STOP

一次i2c message传输完成后强制发送结束信号P,需要i2c_algorithm的I2C_FUNC_PROTOCOL_MANGLING支持

I2C_M_NO_RD_ACK

读操作的时候忽略所有的ACK/NACK信号

SMBus Protocol

SMBus (System Management Bus) 是I2C协议的一个sibling protocol,但SMBus Protocol的电气特性更加严格,而standard I2C Protocol则更为通用,因而通常可以使用标准I2C bus模拟 SMBus,即标准 I2C 协议兼容SMBus协议,但SMBus协议不兼容标准I2C协议

有些I2C controller只支持SMBus协议,而不支持标准I2C协议,而支持标准I2C协议的I2C controller通常也支持SMBus协议,因而为了提高驱动的可移植性,应尽可能使用SMBus协议进行数据传输

Packet Error Checking (PEC)

PEC 在停止位P之前增加一个CRC-8 error-checking byte,以对传输的数据进行校验

I2C_FUNC_SMBUS_QUICK

I2C_FUNC_SMBUS_QUICK = I2C_SMBUS_WRITE I2C_SMBUS_QUICK

1
A Addr Rd/Wr [A] P

只向slave device发送一个 Rd/Wr bit,通常用于判断指定i2c地址处是否有相应的i2c设备存在

quick_command-c250

I2C_FUNC_SMBUS_READ_BYTE

I2C_FUNC_SMBUS_READ_BYTE = I2C_SMBUS_READ I2C_SMBUS_BYTE

1
S Addr Rd [A] [Data] NA P

i2c_smbus_read_byte(),只从slave device读取一个字节的数据,而不指定需要读取的寄存器的地址,通常用于一些结构非常简单的slave device或在之前的i2c message中已经指定该寄存器的地址

read_byte-c450

I2C_FUNC_SMBUS_WRITE_BYTE

I2C_FUNC_SMBUS_WRITE_BYTE = I2C_SMBUS_WRITE I2C_SMBUS_BYTE

1
S Addr Wr [A] Data [A] P

i2c_smbus_write_byte(),只向slave device写入一个字节的数据,而不指定需要读取的寄存器的地址

write_byte-c450

I2C_FUNC_SMBUS_READ_BYTE_DATA

I2C_FUNC_SMBUS_READ_BYTE_DATA = I2C_SMBUS_READ I2C_SMBUS_BYTE_DATA

1
S Addr Wr [A] Comm [A] S Addr Rd [A] [Data] NA P

i2c_smbus_read_byte_data(),从slave device的特定寄存器读取一个字节的数据,寄存器的地址由Comm指定

read_byte_data-c550

I2C_FUNC_SMBUS_WRITE_BYTE_DATA

I2C_FUNC_SMBUS_WRITE_BYTE_DATA = I2C_SMBUS_WRITE I2C_SMBUS_BYTE_DATA

1
S Addr Wr [A] Comm [A] Data [A] P

i2c_smbus_write_byte_data(),向slave device的特定寄存器写入一个字节的数据,寄存器的地址由Comm指定

write_byte_data-c550

I2C_FUNC_SMBUS_READ_WORD_DATA

I2C_FUNC_SMBUS_READ_WORD_DATA = I2C_SMBUS_READ I2C_SMBUS_WORD_DATA

1
S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P

i2c_smbus_read_word_data(),从slave device的特定寄存器读取一个字(word,16 bit)的数据,寄存器的地址由Comm指定

read_word_data-c550

I2C_FUNC_SMBUS_WRITE_WORD_DATA

I2C_FUNC_SMBUS_WRITE_WORD_DATA = I2C_SMBUS_WRITE I2C_SMBUS_WORD_DATA

1
S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] P

i2c_smbus_write_word_data(),向slave device的特定寄存器写入一个字(word,16 bit)的数据,寄存器的地址由Comm指定

write_word_data-c550

I2C_FUNC_SMBUS_READ_BLOCK_DATA

I2C_FUNC_SMBUS_READ_BLOCK_DATA = I2C_SMBUS_READ I2C_SMBUS_BLOCK_DATA

1
2
S Addr Wr [A] Comm [A] 
S Addr Rd [A] [Count] A [Data] A [Data] A ... A [Data] NA P

i2c_smbus_read_block_data(),从slave device的特定寄存器读取一块数据(最多32 bytes),寄存器的地址由Comm指定

read_block_data-c550

I2C_FUNC_SMBUS_WRITE_BLOCK_DATA

I2C_FUNC_SMBUS_WRITE_BLOCK_DATA = I2C_SMBUS_WRITE I2C_SMBUS_BLOCK_DATA

1
S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] ... [A] Data [A] P

i2c_smbus_write_block_data(),向slave device的特定寄存器写入一块数据(最多32 bytes),寄存器的地址由Comm指定

write_block_data-c550

I2C_FUNC_SMBUS_PROC_CALL

I2C_FUNC_SMBUS_PROC_CALL = I2C_SMBUS_READ I2C_SMBUS_PROC_CALL

1
2
S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] 
S Addr Rd [A] [DataLow] A [DataHigh] NA P

SMBus Process Call用于向特定寄存器写入16 bit数据,之后再从该寄存器读取16 bit数据,寄存器的地址由Comm指定

process_call-c550

I2C_FUNC_SMBUS_BLOCK_PROC_CALL

I2C_FUNC_SMBUS_BLOCK_PROC_CALL = I2C_SMBUS_READ I2C_SMBUS_BLOCK_PROC_CALL

1
2
S Addr Wr [A] Comm [A] Count [A] Data [A] ...
S Addr Rd [A] [Count] A [Data] ... A P

BlockWrite - BlockRead Process Call向特定寄存器写入1~32 bytes数据,之后再从该寄存器读取1~32 bytes数据,寄存器的地址由Comm指定

block_write_block_read_process_call-c600

SMBus Host Notify

SMBus Host Notify用于当一个SMBus device成为master device时,由该master device向host device(通常为SMBus controller)发送 16 bit 的数据,即alerting事件

host device是指specialized master device,即与CPU直接相连的master device

1
[S] [HostAddr] [Wr] A [DevAddr] A [DataLow] A [DataHigh] A [P]

其中HostAddr为host device的地址,DevAddr为slave device的地址

host_notify-c550

I2C_FUNC_SMBUS_READ_I2C_BLOCK

I2C_FUNC_SMBUS_READ_I2C_BLOCK = I2C_SMBUS_READ I2C_SMBUS_I2C_BLOCK_DATA

1
2
S Addr Wr [A] Comm [A] 
S Addr Rd [A] [Data] A [Data] A ... A [Data] NA P

i2c_smbus_read_i2c_block_data(),由于standard I2C protocol自身以字节为单位进行数据传输,因而通过SMBus layer实现I2C block data数据传输,从slave device的特定寄存器读取一块数据,寄存器的地址由Comm指定

I2C block data本身不存在字节数的限制,然而由于SMBus protocol的block data存在32 bytes的限制,因而通过SMBus layer实现I2C block data数据传输时I2C block data也被限制为最多32个字节

I2C_FUNC_SMBUS_WRITE_I2C_BLOCK

I2C_FUNC_SMBUS_WRITE_I2C_BLOCK = I2C_SMBUS_WRITE I2C_FUNC_SMBUS_WRITE_I2C_BLOCK

1
S Addr Wr [A] Comm [A] Data [A] Data [A] ... [A] Data [A] P

i2c_smbus_write_i2c_block_data(),通过SMBus layer实现I2C block data数据传输,向slave device的特定寄存器写入一块数据(不存在32 bytes的限制),寄存器的地址由Comm指定

I2C Framework

i2c_sw_topology-c600

  1. I2C Controller
    每个 I2C Controller 都作为一个 platform device 挂载在 platform bus 上,并对应一个 platform driver
    platform bus 通常用于挂载与CPU通过系统总线直接连接的外设

  2. I2C Slave Device
    I2C Core 抽象出 I2C Bus 用来挂载所有与 I2C Controller 通过 I2C 总线相连接的 I2C Slave Device

  3. I2C Adapter
    I2C Core 抽象出一个虚拟实体 I2C Adapter(i2c_adapter, /sys/bus/i2c/devices/i2c-n)用于抽象 I2C Controller 关于数据收发的功能,I2C Adapter 挂载在 I2C Bus 上

相关的数据结构与sysfs entry有

  1. platform bus

    • struct bus_type platform_bus_type
    • /sys/bus/platform
  2. I2C Bus

    • struct bus_type i2c_bus_type
    • /sys/bus/i2c
  3. I2C Controller

    • /sys/bus/platform/devices/xxxxxxxx.i2c
  4. I2C Adapter

    • struct i2c_adapter
    • /sys/bus/i2c/devices/i2c-n
  5. I2C Slave Device

    • struct i2c_client
    • /sys/bus/i2c/devices/n-xx

I2C Framework 分为 I2C core、I2C buses、I2C algos 与 I2C muxes 四部分

  1. I2C core
    I2C core 使用 I2C adapter 与 I2C algorithm 抽象 I2C controller,
    使用 I2C client 与 I2C driver 抽象 I2C slave device,
    同时实现 smbus 协议

  2. I2C buses
    I2C buses 是各个 I2C controller driver 的集合

  3. I2C algos
    I2C algos 是一些通用的 I2C algorithm 的集合,I2C algorithm 定义 I2C 协议包的生成方法,通常是硬件相关的,每个 I2C controller 都必须与一个 I2C algorithm 唯一对应

  4. I2C muxes
    I2C muxes 实现 I2C buse 的多路复用功能

数据结构

i2c_adapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
* i2c_adapter is the structure used to identify a physical i2c bus along
* with the access algorithms necessary to access it.
*/

struct i2c_adapter {
struct module *owner;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;

/* data fields that are valid for all devices */
struct rt_mutex bus_lock;

int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */

bool cancel_xfer_on_shutdown;
bool atomic_xfer_only;

int nr;
char name[48];
struct completion dev_released;

struct mutex userspace_clients_lock;
struct list_head userspace_clients;

struct i2c_bus_recovery_info *bus_recovery_info;
unsigned long bus_clk_rate;
};

i2c_adapter 用于抽象 I2C controller 数据收发的功能,标识物理的 I2C 总线

@name 该i2c controller的名称
@nr 该i2c controller对应的I2C bus ID
@dev 该i2c controller对应的device
@algo 该I2C controller对应的 i2c algorithm
@algo_data i2c algorithm使用的数据,在i2c mux机制中该字段用于存储i2c_mux_priv结构,在bit-banging algorithm中该字段用于存储i2c_algo_bit_data结构

@retries I2C 数据包传输失败时允许重发,retries字段规定允许重发的次数
@timeout timeout字段规定允许重发的超时时间,当数据传输过程超过该超时时间时函数不再尝试重发

@userspace_clients 该 adapter 下挂载的所有 i2c_client 链表
@class 该 I2C controller 支持的 slave device 类型
@bus_lock 实现I2C bus独占性访问的mutex
@bus_clk_rate 该I2C bus clock rate
@cancel_xfer_on_shutdown 标志位,描述

class成员描述该i2c adapter支持的slave device类型

class 描述
I2C_CLASS_HWMON 硬件监控类,如lm_sensors
I2C_CLASS_DDC 显示设备的 DDC(Digital Display Chanel)
I2C_CLASS_SPD 存储设备

i2c_algorithm

1
2
3
4
5
6
7
8
struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags,
char read_write, u8 command, int size, union i2c_smbus_data *data);

/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *);
};

i2c_algorithm 用于抽象 I2C controller 的 I2C 协议

@functionality 该回调函数返回特定 I2C adapter 支持的功能
@master_xfer standard I2C protocol的数据传输接口
@smbus_xfer SMBus protocol的数据传输接口,当该接口未定义时使用standard I2C protocol模拟SMBus protocl

functionality()回调函数返回该i2c adapter支持的数据传输相关的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#define I2C_FUNC_I2C                    0x00000001
#define I2C_FUNC_10BIT_ADDR 0x00000002
#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* I2C_M_IGNORE_NAK etc. */
#define I2C_FUNC_SMBUS_PEC 0x00000008
#define I2C_FUNC_NOSTART 0x00000010 /* I2C_M_NOSTART */
#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 */
#define I2C_FUNC_SMBUS_QUICK 0x00010000
#define I2C_FUNC_SMBUS_READ_BYTE 0x00020000
#define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000
#define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000
#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000
#define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000
#define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000
#define I2C_FUNC_SMBUS_PROC_CALL 0x00800000
#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000
#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */
#define I2C_FUNC_I2C_SLAVE_SUPPORT 0x10000000 /* i2c slave support */

#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE)
#define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
#define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA)
#define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)
#define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)

#define I2C_FUNC_SMBUS_EMUL (I2C_FUNC_SMBUS_QUICK | \
I2C_FUNC_SMBUS_BYTE | \
I2C_FUNC_SMBUS_BYTE_DATA | \
I2C_FUNC_SMBUS_WORD_DATA | \
I2C_FUNC_SMBUS_PROC_CALL | \
I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \
I2C_FUNC_SMBUS_I2C_BLOCK | \
I2C_FUNC_SMBUS_PEC)

标志 描述
I2C_FUNC_I2C 支持标准I2C协议
I2C_FUNC_10BIT_ADDR 支持10 bit地址
I2C_FUNC_PROTOCOL_MANGLING 支持非标准的I2C协议
I2C_FUNC_NOSTART 支持不需要发送START信号的I2C传输,可以用于bulk register read/write
I2C_FUNC_SMBUS_xxx 支持SMBus协议
1
2
3
4
static inline u32 i2c_get_functionality(struct i2c_adapter *adap)
{

return adap->algo->functionality(adap);
}

i2c_get_functionality()函数返回特定 I2C adapter 支持的功能

1
2
3
4
static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func)
{
return (func & i2c_get_functionality(adap)) == func;
}

i2c_check_functionality()函数可以检查特定adapter是否支持特定功能func,若支持函数返回1,否则返回0

i2c_client

1
2
3
4
5
6
7
8
9
10
11
struct i2c_client {
unsigned short flags; /* div., see below */
unsigned short addr; /* chip address - NOTE: 7bit */
/* addresses are stored in the _LOWER_ 7 bits */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct i2c_driver *driver; /* and our access routines */
struct device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
};

i2c_client 抽象 I2C slave device

@flags 标志位,描述该 slave device的一些特性
@addr 该slave device的7-bit地址
@name 该slave device名称
@adapter 该slave device所属的i2c_adapter
@driver 该slave device的i2c_driver
@dev 该slave device对应的device
@irq 该slave device的interrupt number
@detected 通过该链表头组织

flags标志位的值有

1
2
3
#define I2C_CLIENT_PEC      0x04    /* Use Packet Error Checking */
#define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */
#define I2C_CLIENT_WAKE 0x80 /* for board_info; true iff can wake */
标志 描述
I2C_CLIENT_PEC 支持 SMBus Packet Error Checking
I2C_CLIENT_TEN 该设备使用10-bit address
I2C_CLIENT_WAKE 该设备具有wakeup的能力

i2c_driver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct i2c_driver {
unsigned int class;

int (*attach_adapter)(struct i2c_adapter *) __deprecated;

int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);

void (*shutdown)(struct i2c_client *);
int (*suspend)(struct i2c_client *, pm_message_t mesg);
int (*resume)(struct i2c_client *);

void (*alert)(struct i2c_client *, unsigned int data);
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

struct device_driver driver;
const struct i2c_device_id *id_table;

int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};

i2c_driver 抽象 I2C driver,即I2C slave device driver

@class 该i2c_driver支持的设备类型
@driver 该i2c_driver对应的device_driver
@id_table 该i2c_driver支持的i2c_device_id列表
@address_list 该i2c_driver支持的i2c地址列表
@clients 该i2c_driver的i2c_client链表

address_list成员描述该i2c driver支持的i2c address table,其数组的最后一个元素应为I2C_CLIENT_END

1
#define I2C_CLIENT_END		0xfffeU

I2C_ADDRS()宏可以用于设置i2c driver的address_list

1
2
#define I2C_ADDRS(addr, addrs...) \
((const unsigned short []){ addr, ## addrs, I2C_CLIENT_END })

i2c_devinfo

1
2
3
4
5
struct i2c_devinfo {
struct list_head list;
int busnum;
struct i2c_board_info board_info;
};

i2c_devinfo抽象slave device的相关信息,内核为设备树或板级文件中描述的每个slave device分配一个i2c_devinfo结构,并用全局链表__i2c_board_list保存所有的i2c_devinfo

@list __i2c_board_list全局链表保存了所有的i2c_devinfo,该i2c_devinfo通过该list字段访问该链表
@busnum 该slave device所属的i2c bus
@board_info 该i2c_devinfo的i2c_board_info,其中保存该slave device的相关信息

i2c_board_info

1
2
3
4
5
6
7
8
9
10
struct i2c_board_info {
char type[I2C_NAME_SIZE];
unsigned short flags;
unsigned short addr;
void *platform_data;
struct dev_archdata *archdata;
struct device_node *of_node;
struct acpi_dev_node acpi_node;
int irq;
};

i2c_board_info抽象slave device的相关信息,通常用于初始化i2c_client实例

@type 该slave device的名称,对应于i2c_client.name
@flags 该slave device的标志位 ,对应于i2c_client.flags
@addr 该slave device的地址,对应于i2c_client.addr
@platform_data 该slave device的platform data,保存在i2c_client.dev.platform_data
@archdata 该slave device的arch data,保存在i2c_client.dev.archdata
@of_node 该slave device的设备节点
@irq 该slave device的interrupt number,对应于i2c_client.irq

i2c_device_id

1
2
3
4
struct i2c_device_id {
char name[I2C_NAME_SIZE];
kernel_ulong_t driver_data; /* Data private to the driver */
};

struct i2c_device_id抽象i2c device id

i2c driver的id_table成员存储该i2c driver支持的i2c device id table,在i2c client与i2c driver的match过程中遍历i2c device id table中的所有i2c device id,并简单地判断i2c_device_id的名称与i2c_client的名称是否相等,若两者的名称相等则匹配成功

i2c_msg

1
2
3
4
5
6
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};

struct i2c_msg抽象 I2C 数据包,I2C协议以 i2c_msg 为单位进行数据传输,一个i2c message可以包含多个字节的数据

@addr I2C slave device 的地址
@flags I2C message的标志位
@len 数据传输的长度,以 byte 为单位
@buf 用于存储需要写入的数据或读取的数据的buf

flags字段的值有

1
2
3
4
5
6
7
8
#define I2C_M_TEN	           0x0010 /* this is a ten bit chip address */
#define I2C_M_RD 0x0001 /* read data, from slave to master */
#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
标志 描述
I2C_M_TEN 支持 10-bit slave device 地址
I2C_M_RD 此次传输为读操作,from slave to master,若不包含该标志,则此次传输为写操作

standard I2C数据传输以i2c message为单位,i2c_msg的flags标志位中的I2C_M_RD标志描述该i2c message传输的方向,若flags标志位中包含I2C_M_RD标志则此次i2 message传输方向为slave -> master,否则其传输方向为master -> slave

i2c_smbus_data

1
2
3
4
5
6
union i2c_smbus_data {
__u8 byte;
__u16 word;
__u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */
/* and one more for user-space compatibility */
};

SMBus protocol数据传输以i2c_smbus_data为单位,一个i2c_smbus_data可以是一个字节byte,或一个字word(即2 bytes),也可以是一个数据块block(max 32 bytes)

当一次传输一个数据块(即一组字节)时,block[]数组描述需要传输的数据块,其中block[0]描述有效数据块(包括block[1],但不包括blobk[0])的长度,其余的元素为需要传输的数据,其中block[1]表示user-specific data,单次传输的数据块的大小限制为I2C_SMBUS_BLOCK_MAX,即32个字节

1
#define I2C_SMBUS_BLOCK_MAX	32  /* As specified in SMBus standard */

I2C framework init

i2c 子系统的初始化顺序为

1
2
3
4
i2c core init 即 i2c_init
-> board-specific init 即 <board_spec>_machine_desc -> init_machine
-> i2c adapter driver init
-> i2c dev init
顺序 函数 initcall 路径
1 i2c_init postcore_initcall(i2c_init); drivers/i2c/i2c-core.c
2 _machine_desc -> init_machine (tegra_t210ref_dt_init) arch_initcall() arch/arm64/mach-tegra/board-t210ref.c
3 _i2c_init_driver (tegra_i2c_init_driver) subsys_initcall(tegra_i2c_init_driver) drivers/i2c/buses/i2c-tegra.c
4 i2c_dev_init module_init(i2c_dev_init) drivers/i2c/i2c-dev.c

在目标板的初始化代码中,对设备树及板级文件进行处理,对每个i2c bus维护一个i2c_board_info数组,数组中的每个i2c_board_info描述一个对应的slave device,之后在i2c子系统的初始化过程中对每个i2c bus调用i2c_register_board_info,为该i2c bus下的每个i2c_board_info分配对应的i2c_devinfo结构体,并将所有的i2c_devinfo添加到__i2c_board_list全局链表中

I2C core init

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
static int __init i2c_init(void)
{

int retval;

retval = bus_register(&i2c_bus_type);
if (retval)
return retval;

i2c_first_dynamic_bus_num_init();

#ifdef CONFIG_I2C_COMPAT
i2c_adapter_compat_class = class_compat_register("i2c-adapter");
if (!i2c_adapter_compat_class) {
retval = -ENOMEM;
goto bus_err;
}
#endif
retval = i2c_add_driver(&dummy_driver);
if (retval)
goto class_err;
return 0;

class_err:
#ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
bus_unregister(&i2c_bus_type);
return retval;
}

postcore_initcall(i2c_init);

i2c_init()完成i2c core的初始化

  • 调用bus_register()注册i2c_bus_type
  • 调用i2c_first_dynamic_bus_num_init()初始化__i2c_first_dynamic_bus_num变量

board-specific init

tegra_t210ref_dt_init()完成board-specific i2c framework的初始化,其中调用bus_register_notifier()注册i2c_bus_type的通知链,之后对i2c时钟进行设置

i2c adapter driver register

由于 i2c controller 作为 platform device,因而drivers/i2c/buses下存放各个 i2c controller 的 platform driver

1
2
3
4
static int __init tegra_i2c_init_driver(void)
{

return platform_driver_register(&tegra_i2c_driver);
}

i2c adapter driver为tegra_i2c_driver

内核初始化阶段对设备树进行解析,将i2c controller作为platform device挂载到platform_bus_type bus,同时tegra_i2c_init_driver()函数将platform_driver挂载到platform_bus_type bus,当匹配成功时执行platform_driver的probe函数

1
2
3
4
5
6
7
8
9
10
11
12
static struct platform_driver tegra_i2c_driver = {
.probe = tegra_i2c_probe,
.remove = tegra_i2c_remove,
.late_shutdown = tegra_i2c_shutdown,
.id_table = tegra_i2c_devtype,
.driver = {
.name = "tegra-i2c",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(tegra_i2c_of_match),
.pm = TEGRA_I2C_PM,
},
};
1
static int tegra_i2c_probe(struct platform_device *pdev)

tegra_i2c_probe()完成i2c adapter的初始化

  • 分配并初始化一个tegra_i2c_dev结构,为该结构分配一些系统资源
  • 初始化tegra_i2c_dev的i2c_adapter成员,设置i2c_adapter的bus ID、algorithm等
  • 调用i2c_add_numbered_adapter()注册tegra_i2c_dev的i2c_adapter
  • 调用of_i2c_register_devices(),为该i2c_adapter下的子设备节点创建一个i2c_board_info结构,其中调用i2c_new_device()根据i2c_board_info创建对应的i2c client

i2c devinit

1
static int __init i2c_dev_init(void)

i2c_dev_init()完成i2c dev init,其中完成 /dev 对应的字符设备的初始化,其操作集合为i2cdev_fops

Summary

device adding

  1. 在系统初始化的早期阶段,相应的i2c master通常尚未初始化,此时通常在板级文件中调用i2c_register_board_info()注册相应的i2c board info,在之后的相应的i2c master的初始化过程中,为注册的i2c board info创建对应的i2c client

  2. i2c client driver的module init过程中,可以调用i2c_new_device()在相应的i2c master下根据特定i2c board info创建对应的i2c client

  3. 在设备树中定义i2c client,通常在设备树根节点下定义i2c master节点,同时定义i2c client节点作为i2c master节点的子节点;设备树解析过程中,将i2c master节点解析为platform device,在对应的platform driver中

    • 调用i2c_add_adapter()或i2c_add_numbered_adapter()注册相应的i2c master
    • 调用of_i2c_register_devices()遍历i2c master节点的所有子节点,并为每个子节点注册对应的i2c device

device matching

  1. 若在设备树中定义i2c client,则首先实现of style的匹配,即遍历driver的of_match_table中的of device id table,若其中的一个of device id与device的of_node具有相同的name、type、compatible等属性,则成功匹配

  2. 之后调用i2c_match_id()实现i2c bus style匹配,i2c driver的id_table成员存储该i2c driver支持的i2c device id table,其匹配过程只是遍历i2c device id table中的所有i2c device id,并简单地判断i2c_device_id的名称与i2c_client的名称是否相等,若两者的名称相等则匹配成功

register configuration

i2c protocol driver必须通过i2c controller通过i2c bus实现i2c device的寄存器配置,可以直接通过i2c message与i2c device进行通讯,也可以使用封装的regmap与i2c device进行通讯

I2C core

include/linux/i2c.hdrivers/i2c/i2c-core.c定义 I2C core

i2c bus operations

i2c core定义i2c bus,根据Linux Device Model,当i2c clien devicet或i2c driver注册到i2c bus时,调用i2c bus的match()实现新注册的i2c client device或i2c driver的匹配,之后调用i2c bus的probe(),其中调用i2c driver自身的probe()

i2c_bus_type
1
2
3
4
5
6
7
8
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
};

所有注册的i2c client与i2c adapter均挂载在i2c bus上

i2c_bus_type 实现有probe()/remove()方法,因而probe过程中将调用i2c bus的probe()回调函数实现i2c device与i2c driver的binding

i2c_device_match
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{

struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;

if (!client)
return 0;

/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;

/* Then ACPI style match */
if (acpi_driver_match_device(dev, drv))
return 1;

driver = to_i2c_driver(drv);
/* match on an id table if there is one */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;

return 0;
}

i2c_device_match()实现i2c client与i2c driver的match

  • 调用of_driver_match_device()实现of style的匹配,遍历driver的of_match_table指向的of device id table,若其中的一个of device id与device的of_node具有相同的name、type、compatible等属性,则成功匹配
  • 调用acpi_driver_match_device()进行ACPI style的匹配
  • 调用i2c_match_id()实现i2c bus style匹配,i2c driver的id_table成员存储该i2c driver支持的i2c device id table,其匹配过程只是遍历i2c device id table中的所有i2c device id,并简单地判断i2c_device_id的名称与i2c_client的名称是否相等,若两者的名称相等则匹配成功
1
2
3
4
5
6
7
8
9
10
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
const struct i2c_client *client)

{

while (id->name[0]) {
if (strcmp(client->name, id->name) == 0)
return id;
id++;
}
return NULL;
}

i2c_match_id()返回i2c device id table中与i2c client同名的一个i2c device id

i2c_device_probe
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
static int i2c_device_probe(struct device *dev)
{

struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
int status;

if (!client)
return 0;

driver = to_i2c_driver(dev->driver);
if (!driver->probe || !driver->id_table)
return -ENODEV;
client->driver = driver;
if (!device_can_wakeup(&client->dev))
device_init_wakeup(&client->dev,
client->flags & I2C_CLIENT_WAKE);
dev_dbg(dev, "probe\n");

status = dev_pm_domain_attach(&client->dev, true);
if (status != -EPROBE_DEFER) {
status = driver->probe(client, i2c_match_id(driver->id_table,
client));
if (status)
dev_pm_domain_detach(&client->dev, true);
}

return status;
}

i2c_device_probe()实现i2c client与i2c driver的binding

  • 实行一些电源管理相关的初始化工作后,调用该device对应的i2c_driver的probe()方法
i2c_for_each_dev
1
2
3
4
5
6
7
8
9
10
int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *))
{
int res;

mutex_lock(&core_lock);
res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);
mutex_unlock(&core_lock);

return res;
}

i2c_for_each_dev()遍历i2c bus的所有i2c device(包括i2c client与i2c adapetr),对于其中的每个i2c device调用fn()函数,data参数为fn()的参数

i2c adapter driver API

1
2
3
4
5
6
7
8
9
10
11
i2c_add_adapter / i2c_add_numbered_adapter
|_ i2c_register_adapter
|_ device_register
|_ i2c_scan_static_board_info
|_ __process_new_adapter
|_ i2c_do_add_adapter
|_ i2c_detect
|_ i2c_detect_address
|_ i2c_default_probe
|_ i2c_driver->detect()
|_ i2c_new_device

i2c_add_adapter()为i2c adapter分配bus ID,并注册该i2c adapter,其注册过程中主要包括

  • 调用device_register()向device model注册该i2c adapter对应的device
  • 调用i2c_scan_static_board_info(),在i2c adapter初始化之前在板级文件中可以静态定义i2c board info,其中的每个i2c board info都描述对应的i2c client device的相关信息,之后i2c adapter初始化时需要为其下的i2c board info创建对应的i2c client
  • 遍历当前注册的i2c driver,对于每个i2c driver调用__process_new_adapter()为当前已经插入的i2c硬件设备创建对应的i2c client

i2c driver的address_list成员保存该i2c driver支持的i2c client的地址列表,因而i2c adapter初始化过程中需要遍历所有当前注册的i2c driver的支持的i2c地址,在i2c_default_probe()调用中通过i2c_smbus_xfer()由该adapter向该i2c地址处发送一个i2c message,若该message收到回应则说明该地址处存在一个插入的可用i2c设备,因而需要为该i2c设备创建对应的i2c client

i2c_add_adapter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int i2c_add_adapter(struct i2c_adapter *adapter)
{

struct device *dev = &adapter->dev;
int id;

if (dev->of_node) {
id = of_alias_get_id(dev->of_node, "i2c");
if (id >= 0) {
adapter->nr = id;
return __i2c_add_numbered_adapter(adapter);
}
}

mutex_lock(&core_lock);
id = idr_alloc(&i2c_adapter_idr, adapter,
__i2c_first_dynamic_bus_num, 0, GFP_KERNEL);
mutex_unlock(&core_lock);
if (id < 0)
return id;

adapter->nr = id;

return i2c_register_adapter(adapter);
}

i2c_add_adapter()为i2c adapter分配bus ID,并注册该i2c adapter,当已经在设备树中定义该i2c adapter的bus ID时,使用设备树中定义的bus ID,否则在所有未分配的bus ID中动态分配一个可用的bus ID

若注册成功则函数返回0,否则返回负的错误码

  • 当设备树中通过”i2c”属性定义该adapter的bus ID时,调用__i2c_add_numbered_adapter()注册adapter
  • 否则调用idr_alloc()动态分配bus ID,并调用i2c_register_adapter()注册adapter
1
2
3
4
5
6
7
8
9
10
11
12
13
static int __i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
int id;

mutex_lock(&core_lock);
id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1,
GFP_KERNEL);
mutex_unlock(&core_lock);
if (id < 0)
return id == -ENOSPC ? -EBUSY : id;

return i2c_register_adapter(adap);
}

__i2c_add_numbered_adapter()注册指定bus ID的i2c adapter,指定的bus ID保存在adapter.nr中,但在调用该函数前尚未向i2c core申请分配该bus ID

  • 调用idr_alloc()分配指定的bus ID
  • 调用i2c_register_adapter()注册该adapter
i2c_add_numbered_adapter
1
2
3
4
5
6
7
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{

if (adap->nr == -1) /* -1 means dynamically assign bus id */
return i2c_add_adapter(adap);

return __i2c_add_numbered_adapter(adap);
}

i2c_add_numbered_adapter()为i2c adapter分配bus ID,并注册该i2c adapter,此时该i2c adapter对应的bus ID已经保存在adapter.nr中

若注册成功则函数返回0,否则返回负的错误码

  • 当指定的bus ID为-1,即动态分配bus ID时,调用i2c_add_adapter()注册adapter
  • 否则调用__i2c_add_numbered_adapter()注册adapter
i2c_register_adapter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
static int i2c_register_adapter(struct i2c_adapter *adap)
{

int res = 0;

/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p))) {
res = -EAGAIN;
goto out_list;
}

/* Sanity checks */
if (unlikely(adap->name[0] == '\0')) {
pr_err("i2c-core: Attempt to register an adapter with "
"no name!\n");
return -EINVAL;
}
if (unlikely(!adap->algo)) {
pr_err("i2c-core: Attempt to register adapter '%s' with "
"no algo!\n", adap->name);
return -EINVAL;
}

rt_mutex_init(&adap->bus_lock);
mutex_init(&adap->userspace_clients_lock);
INIT_LIST_HEAD(&adap->userspace_clients);

/* Set default timeout to 1 second if not already set */
if (adap->timeout == 0)
adap->timeout = HZ;

dev_set_name(&adap->dev, "i2c-%d", adap->nr);
adap->dev.bus = &i2c_bus_type;
adap->dev.type = &i2c_adapter_type;
res = device_register(&adap->dev);
if (res)
goto out_list;

dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);

#ifdef CONFIG_I2C_COMPAT
res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
adap->dev.parent);
if (res)
dev_warn(&adap->dev,
"Failed to create compatibility class link\n");
#endif

/* bus recovery specific initialization */
if (adap->bus_recovery_info) {
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;

if (!bri->recover_bus) {
dev_err(&adap->dev, "No recover_bus() found, not using recovery\n");
adap->bus_recovery_info = NULL;
goto exit_recovery;
}

/* Generic GPIO recovery */
if (bri->recover_bus == i2c_generic_gpio_recovery) {
if (!gpio_is_valid(bri->scl_gpio)) {
dev_err(&adap->dev, "Invalid SCL gpio, not using recovery\n");
adap->bus_recovery_info = NULL;
goto exit_recovery;
}

if (gpio_is_valid(bri->sda_gpio))
bri->get_sda = get_sda_gpio_value;
else
bri->get_sda = NULL;

bri->get_scl = get_scl_gpio_value;
bri->set_scl = set_scl_gpio_value;
} else if (!bri->set_scl || !bri->get_scl) {
/* Generic SCL recovery */
dev_err(&adap->dev, "No {get|set}_gpio() found, not using recovery\n");
adap->bus_recovery_info = NULL;
}
}

exit_recovery:
/* create pre-declared device nodes */
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);

/* Notify drivers */
mutex_lock(&core_lock);
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
mutex_unlock(&core_lock);

return 0;

out_list:
mutex_lock(&core_lock);
idr_remove(&i2c_adapter_idr, adap->nr);
mutex_unlock(&core_lock);
return res;
}

i2c_register_adapter()注册i2c adapter,若注册成功则函数返回0,否则返回负的错误码

  • 检查i2c_adapter的name、algo成员的有效性,即i2c_adapter的name、algo必须存在定义
  • 初始化i2c_adapter的bus_lock、userspace_clients_lock、userspace_clients等字段
  • 初始化i2c_adapter对应的device,设置其name为“i2c-n”,n为dap->nr,其bus为i2c_bus_type,其device_type为i2c_adapter_type
  • 调用device_register()向内核注册i2c_adapter的device,并在/sys/bus/i2c/devices/中创建相应的符号链接/sys/bus/i2c/devices/i2c-n/
  • 若bus ID小于i2c_first_dynamic_bus_num的值,则调用i2c_scan_static_board_info(),为系统初始化阶段板级文件中静态定义的i2c board info创建对应的i2c_client结构体;i2c_first_dynamic_bus_num变量的值为所有静态注册的i2c board info所在的i2c bus number的最大值加1,因而若当前adapter的bus ID小于该变量,则说明该adapter下存在静态定义的i2c board info
  • 调用bus_for_each_drv(),遍历i2c_bus_type下的所有device_driver,并对遍历的每个device_driver调用__process_new_adapter()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{

struct i2c_devinfo *devinfo;

down_read(&__i2c_board_lock);
list_for_each_entry(devinfo, &__i2c_board_list, list) {
if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter,
&devinfo->board_info))
dev_err(&adapter->dev,
"Can't create device at 0x%02x\n",
devinfo->board_info.addr);
}
up_read(&__i2c_board_lock);
}

i2c_scan_static_board_info()遍历__i2c_board_list链表中在板级文件中静态定义的所有i2c board info,当该i2c board info描述的slave device的bus id等于该i2c_adapter的bus id,即slave device与adapter匹配成功时,调用i2c_new_device()在该i2c_adapter下为该slave device创建一个对应的i2c_client结构

1
2
3
4
 static int __process_new_adapter(struct device_driver *d, void *data)
{
return i2c_do_add_adapter(to_i2c_driver(d), data);
}

当新注册i2c adapter时,需要遍历当前注册的所有i2c driver,对每个i2c driver调用i2c_do_add_adapter()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static int i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap)
{

/* Detect supported devices on that bus, and instantiate them */
i2c_detect(adap, driver);

/* Let legacy drivers scan this bus for matching devices */
if (driver->attach_adapter) {
dev_warn(&adap->dev, "%s: attach_adapter method is deprecated\n",
driver->driver.name);
dev_warn(&adap->dev, "Please use another way to instantiate "
"your i2c_client\n");
/* We ignore the return code; if it fails, too bad */
driver->attach_adapter(adap);
}
return 0;
}
  • 实际调用i2c_detect()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
{

const unsigned short *address_list;
struct i2c_client *temp_client;
int i, err = 0;
int adap_id = i2c_adapter_id(adapter);

address_list = driver->address_list;
if (!driver->detect || !address_list)
return 0;

/* Stop here if the classes do not match */
if (!(adapter->class & driver->class))
return 0;

/* Set up a temporary client to help detect callback */
temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (!temp_client)
return -ENOMEM;
temp_client->adapter = adapter;

for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
"addr 0x%02x\n", adap_id, address_list[i]);
temp_client->addr = address_list[i];
err = i2c_detect_address(temp_client, driver);
if (unlikely(err))
break;
}

kfree(temp_client);
return err;
}

i2c_detect()自动探测该i2c adapter下插入的i2c设备

  • 若i2c driver的detect()回调函数不存在定义或i2c driver的address_list即支持的i2c地址列表为空,则函数直接返回0
  • 若i2c adapter支持的class与i2c driver支持的class不同,则函数直接返回0
  • 遍历i2c_driver支持的i2c地址列表,对遍历的每个i2c地址调用i2c_detect_address()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

static int i2c_detect_address(struct i2c_client *temp_client,
struct i2c_driver *driver)

{

struct i2c_board_info info;
struct i2c_adapter *adapter = temp_client->adapter;
int addr = temp_client->addr;
int err;

/* Make sure the address is valid */
err = i2c_check_addr_validity(addr);
if (err) {
dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
addr);
return err;
}

/* Skip if already in use */
if (i2c_check_addr_busy(adapter, addr))
return 0;

/* Make sure there is something at this address */
if (!i2c_default_probe(adapter, addr))
return 0;

/* Finally call the custom detection function */
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = addr;
err = driver->detect(temp_client, &info);
if (err) {
/* -ENODEV is returned if the detection fails. We catch it
here as this isn't an error. */

return err == -ENODEV ? 0 : err;
}

/* Consistency check */
if (info.type[0] == '\0') {
dev_err(&adapter->dev, "%s detection function provided "
"no name for 0x%x\n", driver->driver.name,
addr);
} else {
struct i2c_client *client;

/* Detection succeeded, instantiate the device */
dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n",
info.type, info.addr);
client = i2c_new_device(adapter, &info);
if (client)
list_add_tail(&client->detected, &driver->clients);
else
dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n",
info.type, info.addr);
}
return 0;
}

i2c_detect_address()检查特定i2c地址处是否存在可用i2c client device

  • 调用i2c_check_addr_validity()检查传入的i2c地址的有效性,若该地址无效则函数直接返回0
  • 调用i2c_check_addr_busy()检查该i2c地址处的设备是否正在被使用,若是则函数直接返回0
  • 调用i2c_default_probe()由该i2c adapter向该i2c设备发送一个i2c message以检查该地址处是否存在一个已经插入的i2c设备
  • 若该地址处存在一个插入的i2c设备,则调用i2c_driver的detect()回调函数获取该i2c设备的相关信息并存储在i2c_board_info结构中,之后调用i2c_new_device()为该设备分配对应的i2c_client结构,并将该i2c_client添加到i2c_driver的i2c_client设备链表中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
static int i2c_default_probe(struct i2c_adapter *adap, unsigned short addr)
{

int err;
union i2c_smbus_data dummy;

#ifdef CONFIG_X86
if (addr == 0x73 && (adap->class & I2C_CLASS_HWMON)
&& i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE_DATA))
err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0,
I2C_SMBUS_BYTE_DATA, &dummy);
else
#endif
if (!((addr & ~0x07) == 0x30 || (addr & ~0x0f) == 0x50)
&& i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK))
err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_WRITE, 0,
I2C_SMBUS_QUICK, NULL);
else if (i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE))
err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0,
I2C_SMBUS_BYTE, &dummy);
else {
dev_warn(&adap->dev, "No suitable probing method supported\n");
err = -EOPNOTSUPP;
}

return err >= 0;
}

i2c_default_probe()由该i2c adapter向特定i2c地址处的i2c设备发送一个i2c message以检查该地址处是否存在一个已经插入的i2c设备

  • 若该i2c adapter支持I2C_FUNC_SMBUS_QUICK特性,则向该地址处发送一个quick message
  • 而对于一些特殊的i2c地址,则需要向该地址处读一个字节的数据,即发送 read byte message
  • 若发送的i2c message收到回应,则函数返回正值,否则函数返回-EOPNOTSUPP
i2c_del_adapter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
void i2c_del_adapter(struct i2c_adapter *adap)
{

struct i2c_adapter *found;
struct i2c_client *client, *next;

/* First make sure that this adapter was ever added */
mutex_lock(&core_lock);
found = idr_find(&i2c_adapter_idr, adap->nr);
mutex_unlock(&core_lock);
if (found != adap) {
pr_debug("i2c-core: attempting to delete unregistered "
"adapter [%s]\n", adap->name);
return;
}

/* Tell drivers about this removal */
mutex_lock(&core_lock);
bus_for_each_drv(&i2c_bus_type, NULL, adap,
__process_removed_adapter);
mutex_unlock(&core_lock);

/* Remove devices instantiated from sysfs */
mutex_lock_nested(&adap->userspace_clients_lock,
i2c_adapter_depth(adap));
list_for_each_entry_safe(client, next, &adap->userspace_clients,
detected) {
dev_dbg(&adap->dev, "Removing %s at 0x%x\n", client->name,
client->addr);
list_del(&client->detected);
i2c_unregister_device(client);
}
mutex_unlock(&adap->userspace_clients_lock);

/* Detach any active clients. This can't fail, thus we do not
* check the returned value. This is a two-pass process, because
* we can't remove the dummy devices during the first pass: they
* could have been instantiated by real devices wishing to clean
* them up properly, so we give them a chance to do that first. */

device_for_each_child(&adap->dev, NULL, __unregister_client);
device_for_each_child(&adap->dev, NULL, __unregister_dummy);

#ifdef CONFIG_I2C_COMPAT
class_compat_remove_link(i2c_adapter_compat_class, &adap->dev,
adap->dev.parent);
#endif

/* device name is gone after device_unregister */
dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name);

/* clean up the sysfs representation */
init_completion(&adap->dev_released);
device_unregister(&adap->dev);

/* wait for sysfs to drop all references */
wait_for_completion(&adap->dev_released);

/* free bus id */
mutex_lock(&core_lock);
idr_remove(&i2c_adapter_idr, adap->nr);
mutex_unlock(&core_lock);

/* Clear the device structure in case this adapter is ever going to be
added again */

memset(&adap->dev, 0, sizeof(adap->dev));
}

i2c_del_adapter()注销i2c adapter

i2c_lock_adapter
1
2
3
4
5
6
7
8
9
void i2c_lock_adapter(struct i2c_adapter *adapter)
{

struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);

if (parent)
i2c_lock_adapter(parent);
else
rt_mutex_lock(&adapter->bus_lock);
}

i2c_lock_adapter()获取该I2C bus的独占性访问(exclusive access),可能使进程进入睡眠状态

i2c_trylock_adapter
1
2
3
4
5
6
7
8
9
static int i2c_trylock_adapter(struct i2c_adapter *adapter)
{

struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);

if (parent)
return i2c_trylock_adapter(parent);
else
return rt_mutex_trylock(&adapter->bus_lock);
}

i2c_trylock_adapter()尝试获取该I2C bus的独占性访问(exclusive access),当前获取独占性访问失败时函数立即返回,因而可以适用于interrupt context

i2c_unlock_adapter
1
2
3
4
5
6
7
8
9
void i2c_unlock_adapter(struct i2c_adapter *adapter)
{

struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);

if (parent)
i2c_unlock_adapter(parent);
else
rt_mutex_unlock(&adapter->bus_lock);
}

i2c_unlock_adapter()释放该I2C bus的独占性访问(exclusive access)

i2c_set_adapter_bus_clk_rate
1
2
3
4
5
6
7
8
int i2c_set_adapter_bus_clk_rate(struct i2c_adapter *adap, int bus_rate)
{

i2c_lock_adapter(adap);
adap->bus_clk_rate = bus_rate;
i2c_unlock_adapter(adap);

return 0;
}

i2c_set_adapter_bus_clk_rate()设置该I2C bus的clock rate

i2c_get_adapter_bus_clk_rate
1
2
3
4
5
6
7
8
9
10
int i2c_get_adapter_bus_clk_rate(struct i2c_adapter *adap)
{

int bus_clk_rate;

i2c_lock_adapter(adap);
bus_clk_rate = adap->bus_clk_rate;
i2c_unlock_adapter(adap);

return bus_clk_rate;
}

i2c_get_adapter_bus_clk_rate()返回该I2C bus的当前clock rate

i2c_shutdown_adapter
1
2
3
4
5
6
void i2c_shutdown_adapter(struct i2c_adapter *adapter)
{

i2c_lock_adapter(adapter);
adapter->cancel_xfer_on_shutdown = true;
i2c_unlock_adapter(adapter);
}

i2c_shutdown_adapter()表示当前adapter即将停止工作,通过设置cancel_xfer_on_shutdown标志位以通知i2c core接下来不要再受理i2c message的传输工作

i2c_shutdown_clear_adapter
1
2
3
4
5
6
7
void i2c_shutdown_clear_adapter(struct i2c_adapter *adapter)
{

i2c_lock_adapter(adapter);
adapter->cancel_xfer_on_shutdown = false;
adapter->atomic_xfer_only = true;
i2c_unlock_adapter(adapter);
}

i2c_shutdown_clear_adapter()通过清除cancel_xfer_on_shutdown标志,同时设置atomic_xfer_only标志位,以恢复i2c message的传输

i2c_get_adapdata
1
2
3
4
static inline void *i2c_get_adapdata(const struct i2c_adapter *dev)
{

return dev_get_drvdata(&dev->dev);
}

i2c_get_adapdata()返回i2c adapter的driver data

i2c_set_adapdata
1
2
3
4
static inline void i2c_set_adapdata(struct i2c_adapter *dev, void *data)
{

dev_set_drvdata(&dev->dev, data);
}

i2c_set_adapdata()设置i2c adapter的driver data

i2c_parent_is_i2c_adapter
1
2
3
4
5
6
7
8
9
10
static inline struct i2c_adapter *
i2c_parent_is_i2c_adapter(const struct i2c_adapter *adapter)
{

struct device *parent = adapter->dev.parent;

if (parent != NULL && parent->type == &i2c_adapter_type)
return to_i2c_adapter(parent);
else
return NULL;
}

i2c_parent_is_i2c_adapter()检查当前i2c adapter的parent device是否对应于一个i2c adapter,若是则返回其parent device对应的i2c adapter,否则返回NULL

i2c_get_adapter
1
2
3
4
5
6
7
8
9
10
11
12
struct i2c_adapter *i2c_get_adapter(int nr)
{

struct i2c_adapter *adapter;

mutex_lock(&core_lock);
adapter = idr_find(&i2c_adapter_idr, nr);
if (adapter && !try_module_get(adapter->owner))
adapter = NULL;

mutex_unlock(&core_lock);
return adapter;
}

i2c_get_adapter()根据bus number返回对应的i2c adapter,并增加该adapter对应的module的引用计数,若该bus number对应的i2c adapter不存在或增加module的引用计数失败,则返回NULL

i2c_put_adapter
1
2
3
4
void i2c_put_adapter(struct i2c_adapter *adap)
{

module_put(adap->owner);
}

i2c_put_adapter()减小该i2c adapter对应的module的引用计数

i2c_clients_command
1
2
3
4
5
6
7
8
void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg)
{

struct i2c_cmd_arg cmd_arg;

cmd_arg.cmd = cmd;
cmd_arg.arg = arg;
device_for_each_child(&adap->dev, &cmd_arg, i2c_cmd);
}

i2c_clients_command()遍历该i2c adapter下的所有i2c client,对其中的每个i2c client调用i2c_cmd(),实际调用该i2c cleint对应的device driver的command()回调函数以执行特定的动作,传入的cmd参数表示需要执行的命令,arg参数表示执行该命令的参数

1
2
3
4
5
6
7
8
9
static int i2c_cmd(struct device *dev, void *_arg)
{

struct i2c_client *client = i2c_verify_client(dev);
struct i2c_cmd_arg *arg = _arg;

if (client && client->driver && client->driver->command)
client->driver->command(client, arg->cmd, arg->arg);
return 0;
}

i2c client driver API

i2c_new_device
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{

struct i2c_client *client;
int status;

client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;

client->adapter = adap;

client->dev.platform_data = info->platform_data;

if (info->archdata)
client->dev.archdata = *info->archdata;

client->flags = info->flags;
client->addr = info->addr;
client->irq = info->irq;

strlcpy(client->name, info->type, sizeof(client->name));

/* Check for address validity */
status = i2c_check_client_addr_validity(client);
if (status) {
dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
goto out_err_silent;
}

/* Check for address business */
status = i2c_check_addr_busy(adap, client->addr);
if (status)
goto out_err;

client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
client->dev.of_node = info->of_node;
ACPI_HANDLE_SET(&client->dev, info->acpi_node.handle);

/* For 10-bit clients, add an arbitrary offset to avoid collisions */
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
client->addr | ((client->flags & I2C_CLIENT_TEN)
? 0xa000 : 0));
status = device_register(&client->dev);
if (status)
goto out_err;

dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
client->name, dev_name(&client->dev));

return client;

out_err:
dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
"(%d)\n", client->name, client->addr, status);
out_err_silent:
kfree(client);
return NULL;
}

i2c_new_device()由i2c board info在i2c adapter下动态创建一个对应的i2c client

若成功函数返回创建的i2c client,否则函数返回NULL

@adap 管理该slave device的i2c_adapter
@info 描述该slave device信息的i2c_board_info结构,包括该slave device的i2c地址、platform data等信息

  • 动态分配一个i2c_client结构,并初始化该i2c_client,设置其adapter字段为传入的i2c_adapter,并根据传入的i2c_board_info设置i2c client的name、addr、irq、flags、dev.platform_data、dev.archdata等字段
  • 调用i2c_check_client_addr_validity()检查该i2c地址是否有效
  • 调用i2c_check_addr_busy()检查该i2c地址是否正在被使用
  • 初始化该i2c_client的device字段,设置该device的parent字段为adap->dev,bus字段为i2c_bus_type,type字段为i2c_client_type,of_node字段为info->of_node,device的名称为 bus_id - i2c_addr
  • 调用device_register()向内核注册该i2c_client的device
i2c_new_probed_device
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
struct i2c_client *
i2c_new_probed_device(struct i2c_adapter *adap,
struct i2c_board_info *info,
unsigned short const *addr_list,
int (*probe)(struct i2c_adapter *, unsigned short addr))
{
int i;

if (!probe)
probe = i2c_default_probe;

for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) {
/* Check address validity */
if (i2c_check_addr_validity(addr_list[i]) < 0) {
dev_warn(&adap->dev, "Invalid 7-bit address "
"0x%02x\n", addr_list[i]);
continue;
}

/* Check address availability */
if (i2c_check_addr_busy(adap, addr_list[i])) {
dev_dbg(&adap->dev, "Address 0x%02x already in "
"use, not probing\n", addr_list[i]);
continue;
}

/* Test address responsiveness */
if (probe(adap, addr_list[i]))
break;
}

if (addr_list[i] == I2C_CLIENT_END) {
dev_dbg(&adap->dev, "Probing failed, no device found\n");
return NULL;
}

info->addr = addr_list[i];
return i2c_new_device(adap, info);
}

i2c_new_probed_device()若不确定i2c client的i2c地址,而是只知道该i2c client的地址在传入的addr_list中,则由i2c core实现该i2c client的地址探测,并由i2c board info在i2c adapter下创建一个对应的i2c client

传入的probe()参数为地址探测时使用的回调函数,探测成功时该回调函数应该返回1,否则返回0;该参数可选,当该参数未定义时,使用i2c_default_probe()作为默认的探测函数

若成功函数返回创建的i2c client,否则函数返回NULL

@adap 管理该slave device的i2c_adapter
@info 描述该slave device信息的i2c_board_info结构,包括该slave device的i2c地址、platform data等信息
@addr_list 需要创建的i2c client的i2c地址范围
@probe 地址探测时需要调用的回调函数

  • 若传入的probe()参数为空,则使用i2c_default_probe()作为默认的探测函数
  • 遍历传入的addr_list中的i2c address table,对于其中的每个i2c地址
    • 调用i2c_check_client_addr_validity()检查该i2c地址是否有效
    • 调用i2c_check_addr_busy()检查该i2c地址是否正在被使用
    • 调用probe()回调函数检测当前遍历的i2c地址是否为该i2c client的地址
    • 若地址探测成功,则将该地址保存在传入的i2c board info的addr字段,之后调用i2c_new_device()注册该i2c client
i2c_new_dummy
1
2
3
4
5
6
7
8
struct i2c_client *i2c_new_dummy(struct i2c_adapter *adapter, u16 address)
{

struct i2c_board_info info = {
I2C_BOARD_INFO("dummy", address),
};

return i2c_new_device(adapter, &info);
}

若一个i2c设备支持多个i2c地址,则调用i2c_new_dummy()创建该i2c设备对应的i2c client

若成功函数返回创建的i2c client,否则函数返回NULL

i2c_unregister_device
1
2
3
4
void i2c_unregister_device(struct i2c_client *client)
{

device_unregister(&client->dev);
}

i2c_unregister_device()注销i2c client

i2c_verify_client
1
2
3
4
5
6
struct i2c_client *i2c_verify_client(struct device *dev)
{

return (dev->type == &i2c_client_type)
? to_i2c_client(dev)
: NULL;
}

i2c_verify_client()通常用于在device model iteration中检查当前遍历的device是否对应一个i2c client,若是则返回其对应的i2c client,否则返回NULL

i2c_verify_adapter
1
2
3
4
5
6
struct i2c_adapter *i2c_verify_adapter(struct device *dev)
{

return (dev->type == &i2c_adapter_type)
? to_i2c_adapter(dev)
: NULL;
}

i2c_verify_adapter()通常用于在device model iteration中检查当前遍历的device是否对应一个i2c adapter,若是则返回其对应的i2c client,否则返回NULL

i2c_get_clientdata
1
2
3
4
static inline void *i2c_get_clientdata(const struct i2c_client *dev)
{

return dev_get_drvdata(&dev->dev);
}

i2c_get_clientdata()返回i2c client的driver data

i2c_set_clientdata
1
2
3
4
static inline void i2c_set_clientdata(struct i2c_client *dev, void *data)
{

dev_set_drvdata(&dev->dev, data);
}

i2c_set_clientdata()设置i2c client的driver data

i2c_use_client
1
2
3
4
5
6
struct i2c_client *i2c_use_client(struct i2c_client *client)
{
if (client && get_device(&client->dev))
return client;
return NULL;
}

i2c_use_client()若该i2c client存在则增加其引用计数,并返回该i2c client,否则返回NULL

i2c_release_client
1
2
3
4
5
void i2c_release_client(struct i2c_client *client)
{

if (client)
put_device(&client->dev);
}

i2c_release_client()减小i2c client的引用计数

i2c_register_driver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{

int res;

/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p)))
return -EAGAIN;

/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;

/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
*/

res = driver_register(&driver->driver);
if (res)
return res;

/* Drivers should switch to dev_pm_ops instead. */
if (driver->suspend)
pr_warn("i2c-core: driver [%s] using legacy suspend method\n",
driver->driver.name);
if (driver->resume)
pr_warn("i2c-core: driver [%s] using legacy resume method\n",
driver->driver.name);

pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);

INIT_LIST_HEAD(&driver->clients);
/* Walk the adapters that are already present */
i2c_for_each_dev(driver, __process_new_driver);

return 0;
}

i2c_register_driver()注册i2c_driver

@owner 注册到该模块
@driver 需要注册的i2c_driver

  • 设置该i2c_driver的device_driver的bus_type成员为i2c_bus_type
  • 调用driver_register()注册该i2c_driver的device_driver
  • 初始化该i2c_driver的i2c_client设备链表
  • 调用i2c_for_each_dev()与__process_new_driver(),遍历当前已经注册的i2c adapter,对于其中的每个i2c adapter调用i2c_do_add_adapter()
1
2
3
4
5
6
static int __process_new_driver(struct device *dev, void *data)
{
if (dev->type != &i2c_adapter_type)
return 0;
return i2c_do_add_adapter(data, to_i2c_adapter(dev));
}

__process_new_driver()遍历当前注册的所有i2c adapter,对其中的每个i2c adapter调用i2c_do_add_adapter()

i2c_add_driver
1
2
#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)

i2c_add_driver()注册i2c_driver,实际调用i2c_register_driver()

i2c_del_driver
1
2
3
4
5
6
7
void i2c_del_driver(struct i2c_driver *driver)
{

i2c_for_each_dev(driver, __process_removed_driver);

driver_unregister(&driver->driver);
pr_debug("i2c-core: driver [%s] unregistered\n", driver->driver.name);
}

i2c_del_driver()注销i2c driver

  • 通过调用__process_removed_driver()遍历当前注册的所有i2c adapter,对于其中的每个i2c adapter调用i2c_do_del_adapter(),其中注销该i2c driver支持的所有i2c client
  • 调用driver_unregister()注销该i2c driver对应的device driver
1
2
3
4
5
6
static int __process_removed_driver(struct device *dev, void *data)
{
if (dev->type == &i2c_adapter_type)
i2c_do_del_adapter(data, to_i2c_adapter(dev));
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static void i2c_do_del_adapter(struct i2c_driver *driver,
struct i2c_adapter *adapter)

{

struct i2c_client *client, *_n;

/* Remove the devices we created ourselves as the result of hardware
* probing (using a driver's detect method) */

list_for_each_entry_safe(client, _n, &driver->clients, detected) {
if (client->adapter == adapter) {
dev_dbg(&adapter->dev, "Removing %s at 0x%x\n",
client->name, client->addr);
list_del(&client->detected);
i2c_unregister_device(client);
}
}
}

i2c board info operations

CONFIG_I2C_BOARDINFO配置项用于配置i2c board info support,即在i2c adapter初始化之前,系统初始化过程中可以调用i2c_register_board_info()向i2c core静态注册i2c board info table,之后当i2c adapter初始化过程中,i2c core会根据注册的i2c board info创建相应的i2c client

drivers/i2c/i2c-boardinfo.c中定义i2c board info support

__i2c_board_list 链表
1
LIST_HEAD(__i2c_board_list);

__i2c_board_list 全局链表用于管理所有注册的i2c board info (devinfo)

1
int __i2c_first_dynamic_bus_num;

__i2c_first_dynamic_bus_num 变量的值为通过i2c_register_board_info()注册的所有i2c board info所在的i2c bus number的最大值加1

I2C_BOARD_INFO
1
2
#define I2C_BOARD_INFO(dev_type, dev_addr) \
.type = dev_type, .addr = (dev_addr)

I2C_BOARD_INFO()宏静态初始化i2c_board_info

i2c_register_board_info
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info,                                   
unsigned len)

{

int status;

down_write(&__i2c_board_lock);

/* dynamic bus numbers will be assigned after the last static one */
if (busnum >= __i2c_first_dynamic_bus_num)
__i2c_first_dynamic_bus_num = busnum + 1;

for (status = 0; len; len--, info++) {
struct i2c_devinfo *devinfo;

devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
if (!devinfo) {
pr_debug("i2c-core: can't register boardinfo!\n");
status = -ENOMEM;
break;
}

devinfo->busnum = busnum;
devinfo->board_info = *info;
list_add_tail(&devinfo->list, &__i2c_board_list);
}

up_write(&__i2c_board_lock);

return status;
}

i2c_register_board_info()通常用于在板级文件中静态注册i2c board info

函数返回成功注册的i2c board info的数量,失败返回-ENOMEM

@busnum 注册的i2c board info所在的i2c bus的bus number
@info 需要注册的i2c_board_info数组
@len 需要注册的i2c_board_info数组的长度

  • 遍历传入的i2c board info table,为其中的每个i2c_board_info分配一个i2c_devinfo结构体,并将创建的i2c_devinfo添加到__i2c_board_list全局链表中

standard I2C protocol data transfer

standard I2C 数据传输的单位为 i2c_msg,一个i2c message可以包含多个字节的数据

可以通过i2c_master_send()或i2c_master_recv()接口进行简单的i2c数据传输,每次只传输一个i2c_msg

同时也可以通过 i2c_transfer()接口进行复杂的i2c数据传输,每次可以传输一个或多个i2c_msg,一次传输过程中的不同i2c message可以指向多个不同的目标slave device的i2c地址

i2c_master_send
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
{

int ret;
struct i2c_adapter *adap = client->adapter;
struct i2c_msg msg;

msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.len = count;
msg.buf = (char *)buf;

ret = i2c_transfer(adap, &msg, 1);

/*
* If everything went ok (i.e. 1 msg transmitted), return #bytes
* transmitted, else error code.
*/

return (ret == 1) ? count : ret;
}

i2c_master_send()向目标i2c_client传输一个i2c_msg数据

@client 数据传输的目标slave device
@buf 该buf保存需要发送的数据
@count 发送的数据的长度,以byte为单位,由于msg.len为u16,因而传输的数据必须小于64K byte

返回成功传输的数据的字节数,否则返回负的错误码

  • 分配并初始化一个i2c_msg,其中i2c_msg的addr即为i2c_client的addr,i2c_msg的len即为传入的参数count,i2c_msg的buf即为传入的参数buf,若i2c_client的flags包含I2C_M_TEN,则i2c_msg的flags即为I2C_M_TEN,否则i2c_msg的flags为0
  • 调用i2c_transfer()在该i2c_client对应的i2c_adapter上传输上述构造的i2c_msg
i2c_master_recv
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int i2c_master_recv(const struct i2c_client *client, char *buf, int count)
{

struct i2c_adapter *adap = client->adapter;
struct i2c_msg msg;
int ret;

msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.flags |= I2C_M_RD;
msg.len = count;
msg.buf = buf;

ret = i2c_transfer(adap, &msg, 1);

/*
* If everything went ok (i.e. 1 msg received), return #bytes received,
* else error code.
*/

return (ret == 1) ? count : ret;
}

i2c_master_recv 接收目标i2c_client传输的一个i2c_msg数据

@client 数据传输的目标slave device
@buf 该buf保存需要发送的数据
@count 需要读取数据的长度,以byte为单位,由于msg.len为u16,因而传输的数据必须小于64K byte

返回成功传输的数据的字节数,否则返回负的错误码

  • 分配并初始化一个i2c_msg,其中i2c_msg的addr即为i2c_client的地址addr,i2c_msg的len即为传入的参数count,i2c_msg的buf即为传入的参数buf,若i2c_client的flags包含I2C_M_TEN,则i2c_msg的flags即为I2C_M_TEN | I2C_M_RD,否则i2c_msg的flags为I2C_M_RD
  • 调用i2c_transfer()在该i2c_client对应的i2c_adapter上传输上述构造的i2c_msg
i2c_transfer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{

int ret;

/* REVISIT the fault reporting model here is weak:
*
* - When we get an error after receiving N bytes from a slave,
* there is no way to report "N".
*
* - When we get a NAK after transmitting N bytes to a slave,
* there is no way to report "N" ... or to let the master
* continue executing the rest of this combined message, if
* that's the appropriate response.
*
* - When for example "num" is two and we successfully complete
* the first message but get an error part way through the
* second, it's unclear whether that should be reported as
* one (discarding status on the second message) or errno
* (discarding status on the first one).
*/


if (adap->algo->master_xfer) {
#ifdef DEBUG
for (ret = 0; ret < num; ret++) {
dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
"len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
}
#endif

if (in_atomic() || irqs_disabled()) {
ret = i2c_trylock_adapter(adap);
if (!ret)
/* I2C activity is ongoing. */
return -EAGAIN;
} else {
i2c_lock_adapter(adap);
}

if (!adap->cancel_xfer_on_shutdown)
ret = __i2c_transfer(adap, msgs, num);
else {
WARN_ON(1);
ret = -EPERM;
}
i2c_unlock_adapter(adap);

return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -EOPNOTSUPP;
}
}

i2c_transfer()执行标准I2C的数据传输,在特定i2c_adapter上传输一个或多个i2c_msg数据

@adap 由该i2c adapter传输数据
@msgs 需要传输的一个或多个i2c_msg
@num 传输的i2c_msg的数量

返回成功传输的i2c message的个数,否则返回负的错误码

  • 若adapter的algorithm的master_xfer()回调函数存在定义,则执行以下操作,否则函数直接返回-EOPNOTSUPP
  • 若当前处于IRQ disabled即interrupt context,则调用i2c_trylock_adapter()尝试获取I2C bus的独占性访问,否则调用i2c_lock_adapter()获取I2C bus的独占性访问
  • 若当前cancel_xfer_on_shutdown标志位未设置,即当前可以进行i2c message的传输,则调用i2c_transfer()函数,i2c_transfer函数调用i2c_adapter的i2c_algorithm的master_xfer()回调函数对传入的i2c_msg进行传输;否则若cancel_xfer_on_shutdown标志位被设置,说明adapter即将停止工作,此时函数返回-EPERM
  • 调用i2c_unlock_adapter()释放该I2C bus的独占性访问
__i2c_transfer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;

/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (ret = 0, try = 0; try <= adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num);
if (ret != -EAGAIN)
break;
if (time_after(jiffies, orig_jiffies + adap->timeout))
break;
}

return ret;
}

当已经获得I2C bus的独占性访问,即已经获得bus clock时,可以调用__i2c_transfer()执行i2c message的传输工作,由特定adapter传输num数量的i2c message

函数返回成功传输的i2c message的数量,否则返回负的错误码

  • 实际调用该adapter的algorithm的master_xfer()回调函数执行标准I2C的数据传输
  • 若master_xfer()回调函数返回-EAGAIN,则再次调用master_xfer()回调函数尝试再次传输,当传输过程超过adapter的timeout指定的超时时间,或重试次数超过adapter的retries指定的重试次数时,此时数据传输失败,函数返回相应的错误码

SMBus protocol data transfer

SMBus protocol 数据传输的单位为 i2c_smbus_data,一个i2c_smbus_data可以是一个字节byte,或一个字word(即2 bytes),也可以是一个数据块block(max 32 bytes)

可以通过i2c_smbus_xfer()接口传输一个i2c_smbus_data

同时也可以通过其他一系列的variant interface,这些variant interface实际是i2c_smbus_xfer()的封装,执行data size specific的数据传输

i2c_smbus_xfer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
char read_write, u8 command, int protocol, union i2c_smbus_data *data)

{

unsigned long orig_jiffies;
int try;
s32 res;

flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;

if (adapter->algo->smbus_xfer) {
i2c_lock_adapter(adapter);

/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (res = 0, try = 0; try <= adapter->retries; try++) {
res = adapter->algo->smbus_xfer(adapter, addr, flags,
read_write, command,
protocol, data);
if (res != -EAGAIN)
break;
if (time_after(jiffies,
orig_jiffies + adapter->timeout))
break;
}
i2c_unlock_adapter(adapter);

if (res != -EOPNOTSUPP || !adapter->algo->master_xfer)
return res;
/*
* Fall back to i2c_smbus_xfer_emulated if the adapter doesn't
* implement native support for the SMBus operation.
*/

}

return i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
command, protocol, data);
}

i2c_smbus_xfer()执行SMBus protocol的数据传输

@adapter 需要传输数据的 smbus
@addr 数据传输的目标slave device的i2c地址
@flags 数据传输的标志,I2CCLIENT*标志集
@read_write 数据发送或接收的标志位,I2C_SMBUS_READ or I2C_SMBUS_WRITE
@command Byte interpreted by slave,通常表示slave device需要读取或写入的寄存器地址
@protocol 描述需要执行的SMBus protocol operation的类型,如I2C_SMBUS_BYTE_DATA等,通常表示传输的数据的大小
@data 需要传输的数据,SMBus protocol数据传输以i2c_smbus_data为单位

成功返回0,否则返回负的错误码

flags标志位描述slave device的特性,只支持I2C_M_TEN、I2C_CLIENT_PEC、I2C_CLIENT_SCCB标志

I2C_M_TEN 描述slave device使用10 bit i2c address
I2C_CLIENT_PEC 描述slave device支持 SMBus Packet Error Checking

read_write标志位描述数据传输的方向

1
2
#define I2C_SMBUS_READ  1
#define I2C_SMBUS_WRITE 0

I2C_SMBUS_READ 描述数据传输的方向为 slave -> master
I2C_SMBUS_WRITE 描述数据传输的方向为 master -> slave

protocol标志位描述SMBus protocol数据传输的类型,通常表示传输的数据的大小

1
2
3
4
5
6
7
8
9
#define I2C_SMBUS_QUICK		    0
#define I2C_SMBUS_BYTE 1
#define I2C_SMBUS_BYTE_DATA 2
#define I2C_SMBUS_WORD_DATA 3
#define I2C_SMBUS_PROC_CALL 4
#define I2C_SMBUS_BLOCK_DATA 5
#define I2C_SMBUS_I2C_BLOCK_BROKEN 6
#define I2C_SMBUS_BLOCK_PROC_CALL 7 /* SMBus 2.0 */
#define I2C_SMBUS_I2C_BLOCK_DATA 8
  • 传入的flags标志位只支持I2C_M_TEN、I2C_CLIENT_PEC、I2C_CLIENT_SCCB标志
  • 若i2c_adapter的i2c_algorithm的smbus_xfer()回调函数存在定义,则调用smbus_xfer()函数进行SMBus protocol数据传输
    • 调用i2c_lock_adapter()获取该SMBus的独占性访问
    • 调用smbus_xfer()回调函数进行SMBus protocol数据传输
    • 若smbus_xfer()回调函数返回-EAGAIN,则再次调用smbus_xfer()回调函数尝试再次传输,当传输过程超过adapter的timeout指定的超时时间,或重试次数超过adapter的retries指定的重试次数时,此时数据传输失败,不再尝试重发
    • 调用i2c_unlock_adapter()释放该I2C bus的独占性访问
    • 若smbus_xfer()回调函数返回-EOPNOTSUPP,同时该adapter的master_xfer()回调函数存在定义,则调用i2c_smbus_xfer_emulated()尝试使用standard I2C protocol模拟SMBus protocol进行数据传输,否则函数返回相应的错误码
  • 否则调用i2c_smbus_xfer_emulated()函数进行数据传输,其中使用standard I2C protocol模拟SMBus protocol进行数据传输
i2c_probe_func_quick_read
1
2
3
4
5
int i2c_probe_func_quick_read(struct i2c_adapter *adap, unsigned short addr)
{

return i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0,
I2C_SMBUS_QUICK, NULL) >= 0;
}

i2c_probe_func_quick_read()使用smbus协议通过向特定i2c地址处发送一个SMBUS_QUICK message,以检测该地址处是否存在一个i2c设备

i2c_smbus_read_byte
1
2
3
4
5
6
7
8
9
10
s32 i2c_smbus_read_byte(const struct i2c_client *client)
{

union i2c_smbus_data data;
int status;

status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_READ, 0,
I2C_SMBUS_BYTE, &data);
return (status < 0) ? status : data.byte;
}

i2c_smbus_read_byte()使用smbus协议从slave device读取一个字节的数据

成功返回读取的一个字节的数据,否则返回负的错误码

i2c_smbus_write_byte
1
2
3
4
5
s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value)
{

return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL);
}

i2c_smbus_write_byte()使用smbus协议向slave device发送一个字节的数据

成功返回0,否则返回负的错误码

i2c_smbus_read_byte_data
1
2
3
4
5
6
7
8
9
10
s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command)
{

union i2c_smbus_data data;
int status;

status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_READ, command,
I2C_SMBUS_BYTE_DATA, &data);
return (status < 0) ? status : data.byte;
}

i2c_smbus_read_byte_data()使用smbus协议读取slave device的特定寄存器的一个字节的数据,command参数描述读取的寄存器的地址

成功返回读取的一个字节的数据,否则返回负的错误码

i2c_smbus_write_byte_data
1
2
3
4
5
6
7
8
9
s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command,
u8 value)

{

union i2c_smbus_data data;
data.byte = value;
return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_WRITE, command,
I2C_SMBUS_BYTE_DATA, &data);
}

i2c_smbus_write_byte_data()使用smbus协议向slave device的特定寄存器写入一个字节的数据,command参数描述写入的寄存器的地址

成功返回0,否则返回负的错误码

i2c_smbus_read_word_data
1
2
3
4
5
6
7
8
9
10
s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command)
{

union i2c_smbus_data data;
int status;

status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_READ, command,
I2C_SMBUS_WORD_DATA, &data);
return (status < 0) ? status : data.word;
}

i2c_smbus_read_word_data()使用smbus协议读取slave device的特定寄存器的一个字(word,16 bit)的数据,command参数描述读取的寄存器的地址

成功返回读取的一个字的数据,否则返回负的错误码

i2c_smbus_write_word_data
1
2
3
4
5
6
7
8
9
s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command,
u16 value)

{

union i2c_smbus_data data;
data.word = value;
return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_WRITE, command,
I2C_SMBUS_WORD_DATA, &data);
}

i2c_smbus_write_word_data()使用smbus协议向slave device的特定寄存器写入一个字(word,16 bit)的数据,command参数描述写入的寄存器的地址

成功返回0,否则返回负的错误码

i2c_smbus_read_word_swapped
1
2
3
4
5
6
7
static inline s32
i2c_smbus_read_word_swapped(const struct i2c_client *client, u8 command)
{

s32 value = i2c_smbus_read_word_data(client, command);

return (value < 0) ? value : swab16(value);
}

i2c_smbus_read_word_swapped()与i2c_smbus_read_word_data()相类似,只是将读取的一个字(16 bit, 2 bytes)的数据转换字节序之后再返回读取的一个字的数据,即若读取的数据为0x00ff,则返回的数据为0xff00

i2c_smbus_write_word_swapped
1
2
3
4
5
6
static inline s32
i2c_smbus_write_word_swapped(const struct i2c_client *client,
u8 command, u16 value)

{

return i2c_smbus_write_word_data(client, command, swab16(value));
}

i2c_smbus_write_word_swapped()与i2c_smbus_write_word_data()相类似,只是将传入的需要写入的一个字(16 bit, 2 bytes)的数据转换字节序之后再将其写入相应的寄存器中,即若需要写入的数据为0x00ff,则实际写入的数据为0xff00

i2c_smbus_read_block_data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command,
u8 *values)

{

union i2c_smbus_data data;
int status;

status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_READ, command,
I2C_SMBUS_BLOCK_DATA, &data);
if (status)
return status;

memcpy(values, &data.block[1], data.block[0]);
return data.block[0];
}

i2c_smbus_read_block_data()使用smbus协议从slave device的特定寄存器读取一块数据(最多32 bytes),command参数描述读取的寄存器的地址,values指针用于保存读取的数据

返回成功读取的数据的字节数,否则返回负的错误码

i2c_smbus_write_block_data
1
2
3
4
5
6
7
8
9
10
11
12
13
s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command,
u8 length, const u8 *values)

{

union i2c_smbus_data data;

if (length > I2C_SMBUS_BLOCK_MAX)
length = I2C_SMBUS_BLOCK_MAX;
data.block[0] = length;
memcpy(&data.block[1], values, length);
return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_WRITE, command,
I2C_SMBUS_BLOCK_DATA, &data);
}

i2c_smbus_write_block_data()使用smbus协议向slave device的特定寄存器写入一块数据(最多32 bytes),command参数描述写入的寄存器的地址,values指针保存需要写入的数据,length参数为需要写入的数据的字节数

成功返回0,否则返回负的错误码

i2c_smbus_read_i2c_block_data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client, u8 command,
u8 length, u8 *values)

{

union i2c_smbus_data data;
int status;

if (length > I2C_SMBUS_BLOCK_MAX)
length = I2C_SMBUS_BLOCK_MAX;
data.block[0] = length;
status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_READ, command,
I2C_SMBUS_I2C_BLOCK_DATA, &data);
if (status < 0)
return status;

memcpy(values, &data.block[1], data.block[0]);
return data.block[0];
}

i2c_smbus_read_i2c_block_data()通过SMBus layer实现I2C block data数据传输,从slave device的特定寄存器读取一块数据(最多32 bytes),command参数描述读取的寄存器的地址,length参数为需要读取的数据的字节数,values指针用于保存读取的数据

返回成功读取的数据的字节数,否则返回负的错误码

i2c_smbus_write_i2c_block_data
1
2
3
4
5
6
7
8
9
10
11
12
13
s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client, u8 command,
u8 length, const u8 *values)

{

union i2c_smbus_data data;

if (length > I2C_SMBUS_BLOCK_MAX)
length = I2C_SMBUS_BLOCK_MAX;
data.block[0] = length;
memcpy(data.block + 1, values, length);
return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_WRITE, command,
I2C_SMBUS_I2C_BLOCK_DATA, &data);
}

i2c_smbus_write_i2c_block_data()通过SMBus layer实现I2C block data数据传输,向slave device的特定寄存器写入一块数据(最多32 bytes),command参数描述写入的寄存器的地址,values指针保存需要写入的数据,length参数为需要写入的数据的字节数

成功返回0,否则返回负的错误码

i2c_smbus_xfer_emulated
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
unsigned short flags,
char read_write, u8 command, int size,
union i2c_smbus_data *data)

{

/* So we need to generate a series of msgs. In the case of writing, we
need to use only one message; when reading, we need two. We initialize
most things with sane defaults, to keep the code below somewhat
simpler. */

unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];
unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
int num = read_write == I2C_SMBUS_READ ? 2 : 1;
int i;
u8 partial_pec = 0;
int status;
struct i2c_msg msg[2] = {
{
.addr = addr,
.flags = flags,
.len = 1,
.buf = msgbuf0,
}, {
.addr = addr,
.flags = flags | I2C_M_RD,
.len = 0,
.buf = msgbuf1,
},
};

msgbuf0[0] = command;
switch (size) {
case I2C_SMBUS_QUICK:
msg[0].len = 0;
/* Special case: The read/write field is used as data */
msg[0].flags = flags | (read_write == I2C_SMBUS_READ ?
I2C_M_RD : 0);
num = 1;
break;
case I2C_SMBUS_BYTE:
if (read_write == I2C_SMBUS_READ) {
/* Special case: only a read! */
msg[0].flags = I2C_M_RD | flags;
num = 1;
}
break;
case I2C_SMBUS_BYTE_DATA:
if (read_write == I2C_SMBUS_READ)
msg[1].len = 1;
else {
msg[0].len = 2;
msgbuf0[1] = data->byte;
}
break;
case I2C_SMBUS_WORD_DATA:
if (read_write == I2C_SMBUS_READ)
msg[1].len = 2;
else {
msg[0].len = 3;
msgbuf0[1] = data->word & 0xff;
msgbuf0[2] = data->word >> 8;
}
break;
case I2C_SMBUS_PROC_CALL:
num = 2; /* Special case */
read_write = I2C_SMBUS_READ;
msg[0].len = 3;
msg[1].len = 2;
msgbuf0[1] = data->word & 0xff;
msgbuf0[2] = data->word >> 8;
break;
case I2C_SMBUS_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) {
msg[1].flags |= I2C_M_RECV_LEN;
msg[1].len = 1; /* block length will be added by
the underlying bus driver */

} else {
msg[0].len = data->block[0] + 2;
if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {
dev_err(&adapter->dev,
"Invalid block write size %d\n",
data->block[0]);
return -EINVAL;
}
for (i = 1; i < msg[0].len; i++)
msgbuf0[i] = data->block[i-1];
}
break;
case I2C_SMBUS_BLOCK_PROC_CALL:
num = 2; /* Another special case */
read_write = I2C_SMBUS_READ;
if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {
dev_err(&adapter->dev,
"Invalid block write size %d\n",
data->block[0]);
return -EINVAL;
}
msg[0].len = data->block[0] + 2;
for (i = 1; i < msg[0].len; i++)
msgbuf0[i] = data->block[i-1];
msg[1].flags |= I2C_M_RECV_LEN;
msg[1].len = 1; /* block length will be added by
the underlying bus driver */

break;
case I2C_SMBUS_I2C_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) {
msg[1].len = data->block[0];
} else {
msg[0].len = data->block[0] + 1;
if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 1) {
dev_err(&adapter->dev,
"Invalid block write size %d\n",
data->block[0]);
return -EINVAL;
}
for (i = 1; i <= data->block[0]; i++)
msgbuf0[i] = data->block[i];
}
break;
default:
dev_err(&adapter->dev, "Unsupported transaction %d\n", size);
return -EOPNOTSUPP;
}

i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK
&& size != I2C_SMBUS_I2C_BLOCK_DATA);
if (i) {
/* Compute PEC if first message is a write */
if (!(msg[0].flags & I2C_M_RD)) {
if (num == 1) /* Write only */
i2c_smbus_add_pec(&msg[0]);
else /* Write followed by read */
partial_pec = i2c_smbus_msg_pec(0, &msg[0]);
}
/* Ask for PEC if last message is a read */
if (msg[num-1].flags & I2C_M_RD)
msg[num-1].len++;
}

status = i2c_transfer(adapter, msg, num);
if (status < 0)
return status;

/* Check PEC if last message is a read */
if (i && (msg[num-1].flags & I2C_M_RD)) {
status = i2c_smbus_check_pec(partial_pec, &msg[num-1]);
if (status < 0)
return status;
}

if (read_write == I2C_SMBUS_READ)
switch (size) {
case I2C_SMBUS_BYTE:
data->byte = msgbuf0[0];
break;
case I2C_SMBUS_BYTE_DATA:
data->byte = msgbuf1[0];
break;
case I2C_SMBUS_WORD_DATA:
case I2C_SMBUS_PROC_CALL:
data->word = msgbuf1[0] | (msgbuf1[1] << 8);
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
for (i = 0; i < data->block[0]; i++)
data->block[i+1] = msgbuf1[i];
break;
case I2C_SMBUS_BLOCK_DATA:
case I2C_SMBUS_BLOCK_PROC_CALL:
for (i = 0; i < msgbuf1[0] + 1; i++)
data->block[i] = msgbuf1[i];
break;
}
return 0;
}

i2c_smbus_xfer_emulated()使用standard I2C protocol模拟SMBus protocol进行数据传输

@adapter 需要传输数据的 smbus
@addr 数据传输的目标slave device的i2c地址
@flags 数据传输的标志,I2CCLIENT*标志集
@read_write 数据发送或接收的标志位,I2C_SMBUS_READ or I2C_SMBUS_WRITE
@command Byte interpreted by slave,通常表示slave device需要读取或写入的寄存器地址
@size 描述需要执行的SMBus protocol operation的类型,如I2C_SMBUS_BYTE_DATA等,通常表示传输的数据的大小
@data 需要传输的数据

成功返回0,否则返回负的错误码,若当前为读操作,则data参数中保存读取的数据

使用standard I2C protocol模拟SMBus protocol时

  • SMBus protocol的write操作对应一个i2c message,即slave device i2c address与write data的传输方向均为 master -> slave,因而只需一个i2c message
  • SMBus protocol的read操作对应两个i2c message,即slave device i2c address的传输方向为 master -> slave,而write data的传输方向均为 slave -> master,因而需要两个i2c message

i2c_smbus_xfer_emulated()中定义两个i2c message,其中msg[0]的传输方向为master -> slave,msg[1]的传输方向为 slave -> master

  • msg[0]对应的data buffer为msgbuf0[],其中存储SMBus command即操作的寄存器地址
  • msg[1]对应的data buffer为msgbuf1[]

之后只需根据SMBus的不同protocol对i2c message进行设置,之后调用i2c_transfer()传输相应的i2c message

  • 定义两个i2c message msg[2]
  • 将msgbuf0[0]设置为传入的command参数
  • 根据传入的size参数即需要传输的数据大小,设置相应的i2c message
  • 若传入的flags标志位包含I2C_CLIENT_PEC即启用CRC校验,同时传入的size参数不为I2C_SMBUS_QUICK、I2C_SMBUS_I2C_BLOCK_DATA即QUICK、I2C_BLOCK_DATA不支持SMBus PEC,则启用SMBus PEC,并向i2c message中添加CRC byte
  • 调用i2c_transfer()传输相应的i2c message,若传输失败则函数直接返回相应的错误码
  • 若之前启用SMBus PEC,且当前操作为读操作时,对读取的数据进行CRC校验,若校验存在错误则函数直接返回相应的错误码
  • 若当前操作为读操作,则将读取的数据保存在传入的data参数中,最后函数返回0

I2C Sysfs

i2c adapter sysfs
1
2
3
4
struct device_type i2c_adapter_type = {
.groups = i2c_adapter_attr_groups,
.release = i2c_adapter_dev_release,
};

i2c_adapter_type 为i2c adapter对应的device的device_type

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static const struct attribute_group *i2c_adapter_attr_groups[] = {
&i2c_adapter_attr_group,
NULL
};

static struct attribute_group i2c_adapter_attr_group = {
.attrs = i2c_adapter_attrs,
};

static struct attribute *i2c_adapter_attrs[] = {
&dev_attr_name.attr,
&dev_attr_new_device.attr,
&dev_attr_delete_device.attr,
&dev_attr_bus_clk_rate.attr,
NULL
};

i2c adapter对应的 /sys/bus/i2c/devices/i2c-n/ 文件夹下存在name、new_device、delete_device、bus_clk_rate属性文件

name
1
2
3
4
5
6
7
8
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);

static ssize_t
show_name(struct device *dev, struct device_attribute *attr, char *buf)
{

return sprintf(buf, "%s\n", dev->type == &i2c_client_type ?
to_i2c_client(dev)->name : to_i2c_adapter(dev)->name);
}

name属性文件可读,返回对应的i2c adapter的name字段,即该i2c adapter的名称;不可写

new_device
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device);

static ssize_t
i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)

{

struct i2c_adapter *adap = to_i2c_adapter(dev);
struct i2c_board_info info;
struct i2c_client *client;
char *blank, end;
int res;

memset(&info, 0, sizeof(struct i2c_board_info));

blank = strchr(buf, ' ');
if (!blank) {
dev_err(dev, "%s: Missing parameters\n", "new_device");
return -EINVAL;
}
if (blank - buf > I2C_NAME_SIZE - 1) {
dev_err(dev, "%s: Invalid device name\n", "new_device");
return -EINVAL;
}
memcpy(info.type, buf, blank - buf);

/* Parse remaining parameters, reject extra parameters */
res = sscanf(++blank, "%hi%c", &info.addr, &end);
if (res < 1) {
dev_err(dev, "%s: Can't parse I2C address\n", "new_device");
return -EINVAL;
}
if (res > 1 && end != '\n') {
dev_err(dev, "%s: Extra parameters\n", "new_device");
return -EINVAL;
}

client = i2c_new_device(adap, &info);
if (!client)
return -EINVAL;

/* Keep track of the added device */
mutex_lock(&adap->userspace_clients_lock);
list_add_tail(&client->detected, &adap->userspace_clients);
mutex_unlock(&adap->userspace_clients_lock);
dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device",
info.type, info.addr);

return count;
}

new_device属性文件不可读;可写,用于在该i2c adapter下创建特定名称,特定地址的i2c client

写入的字符串的格式为

1
<name> 0x<i2c_address>

其中 为新创建的i2c client的名称,为i2c client的地址

delete_device
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, S_IWUSR, NULL,
i2c_sysfs_delete_device)
;


static ssize_t
i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)

{

struct i2c_adapter *adap = to_i2c_adapter(dev);
struct i2c_client *client, *next;
unsigned short addr;
char end;
int res;

/* Parse parameters, reject extra parameters */
res = sscanf(buf, "%hi%c", &addr, &end);
if (res < 1) {
dev_err(dev, "%s: Can't parse I2C address\n", "delete_device");
return -EINVAL;
}
if (res > 1 && end != '\n') {
dev_err(dev, "%s: Extra parameters\n", "delete_device");
return -EINVAL;
}

/* Make sure the device was added through sysfs */
res = -ENOENT;
mutex_lock_nested(&adap->userspace_clients_lock,
i2c_adapter_depth(adap));
list_for_each_entry_safe(client, next, &adap->userspace_clients,
detected) {
if (client->addr == addr) {
dev_info(dev, "%s: Deleting device %s at 0x%02hx\n",
"delete_device", client->name, client->addr);

list_del(&client->detected);
i2c_unregister_device(client);
res = count;
break;
}
}
mutex_unlock(&adap->userspace_clients_lock);

if (res < 0)
dev_err(dev, "%s: Can't find device in list\n",
"delete_device");
return res;
}

delete_device属性文件不可读;可写,与new_device相类似,用于删除该i2c adapter下的特定的i2c client

写入的字符串的格式为

1
0x<i2c_address>

其中 为需要删除的i2c client的地址

bus_clk_rate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static DEVICE_ATTR(bus_clk_rate, 0644, show_bus_clk_rate, set_bus_clk_rate);

static ssize_t show_bus_clk_rate(struct device *dev,
struct device_attribute *attr, char *buf)

{

struct i2c_adapter *adap = to_i2c_adapter(dev);

return sprintf(buf, "%ld\n", adap->bus_clk_rate);
}

static ssize_t set_bus_clk_rate(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)

{

struct i2c_adapter *adap = to_i2c_adapter(dev);
char *p = (char *)buf;
int bus_clk_rate;

bus_clk_rate = memparse(p, &p);
dev_info(dev, "Setting clock rate %d on next transfer\n", bus_clk_rate);
adap->bus_clk_rate = bus_clk_rate;
return count;
}

bus_clk_rate属性文件可读,返回该i2c adapter的bus_clk_rate;可写,设置该i2c adapter的bus_clk_rate

i2c client sysfs
1
2
3
4
5
static struct device_type i2c_client_type = {
.groups = i2c_dev_attr_groups,
.uevent = i2c_device_uevent,
.release = i2c_client_dev_release,
};

i2c_client_type()为i2c client对应的device的device_type

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static const struct attribute_group *i2c_dev_attr_groups[] = {
&i2c_dev_attr_group,
NULL
};

static struct attribute_group i2c_dev_attr_group = {
.attrs = i2c_dev_attrs,
};

static struct attribute *i2c_dev_attrs[] = {
&dev_attr_name.attr,
/* modalias helps coldplug: modprobe $(cat .../modalias) */
&dev_attr_modalias.attr,
NULL
};

i2c adapter对应的 /sys/bus/i2c/devices/n-xx/ 文件夹下存在name、modalias属性文件

name
1
2
3
4
5
6
7
8
9
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);


static ssize_t
show_name(struct device *dev, struct device_attribute *attr, char *buf)
{

return sprintf(buf, "%s\n", dev->type == &i2c_client_type ?
to_i2c_client(dev)->name : to_i2c_adapter(dev)->name);
}

name属性文件可读,返回对应的i2c client的name字段,即该i2c client的名称;不可写

modalias
1
2
3
4
5
6
7
8
9
10
static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);

static ssize_t
show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
{

struct i2c_client *client = to_i2c_client(dev);
return sprintf(buf, "%s%s\n", I2C_MODULE_PREFIX, client->name);
}

#define I2C_MODULE_PREFIX "i2c:"

modalias属性文件可读,返回对应的i2c client的modalias信息,实际为i2c:<name>,其中 为i2c client的名称;不可写

I2C OF Support

include/linux/of_i2c.hdrivers/of/of_i2c.c中定义OF I2C接口

I2C OF Interface

of_find_i2c_device_by_node
1
2
3
4
5
6
7
8
9
10
11
struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
{

struct device *dev;

dev = bus_find_device(&i2c_bus_type, NULL, node,
of_dev_node_match);
if (!dev)
return NULL;

return i2c_verify_client(dev);
}

of_find_i2c_device_by_node()在当前i2c bus中所有注册的i2c device中寻找特定device node对应的i2c client,若找到则返回该device node对应的i2c client,否则函数返回NULL

由于执行路径中当找到对应的i2c client时会调用调用get_device(),因而该函数返回后当对该i2c client执行完相应的操作之后,应该调用put_device()

of_find_i2c_adapter_by_node
1
2
3
4
5
6
7
8
9
10
11
struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
{

struct device *dev;

dev = bus_find_device(&i2c_bus_type, NULL, node,
of_dev_node_match);
if (!dev)
return NULL;

return i2c_verify_adapter(dev);
}

of_find_i2c_adapter_by_node()在当前i2c bus中所有注册的i2c device中寻找特定device node对应的i2c adapter,若找到则返回该device node对应的i2c adapter,否则函数返回NULL

由于执行路径中当找到对应的i2c adapter时会调用调用get_device(),因而该函数返回后当对该i2c adapter执行完相应的操作之后,应该调用put_device()

of_i2c_register_devices
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
void of_i2c_register_devices(struct i2c_adapter *adap)
{

void *result;
struct device_node *node;

/* Only register child devices if the adapter has a node pointer set */
if (!adap->dev.of_node)
return;

dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");

for_each_available_child_of_node(adap->dev.of_node, node) {
struct i2c_board_info info = {};
struct dev_archdata dev_ad = {};
const __be32 *addr;
int len;

dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);

if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
node->full_name);
continue;
}

addr = of_get_property(node, "reg", &len);
if (!addr || (len < sizeof(int))) {
dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
node->full_name);
continue;
}

info.addr = be32_to_cpup(addr);
if (info.addr > (1 << 10) - 1) {
dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
info.addr, node->full_name);
continue;
}

info.irq = irq_of_parse_and_map(node, 0);
info.of_node = of_node_get(node);
info.archdata = &dev_ad;

if (of_get_property(node, "wakeup-source", NULL))
info.flags |= I2C_CLIENT_WAKE;

request_module("%s%s", I2C_MODULE_PREFIX, info.type);

result = i2c_new_device(adap, &info);
if (result == NULL) {
dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
node->full_name);
of_node_put(node);
irq_dispose_mapping(info.irq);
continue;
}
}
}

当在设备树中定义i2c multiplexer (i2c mux channel) 及其下的i2c client时,调用of_i2c_register_devices()注册设备树中定义的该i2c mux channel下的所有i2c cleint

  • 遍历该i2c mux channel对应的i2c adapter的device的device node的所有child device node,其中的每个child device node表示该i2c mux channel下的一个i2c client,根据child device node中定义的信息初始化该i2c client对应的i2c_board_info
  • 之后根据i2c_board_info结构调用i2c_new_device()为每个child device node创建对应的i2c client

I2C Algorithm

i2c algorithm 描述i2c message传输的底层实现,i2c adapter可以实现自身特定的i2c algorithm,也可以直接使用drivers/i2c/algos/下定义的i2c algorithm

include/linux/i2c-algo-bit.hdrivers/i2c/algos/i2c-algo-bit.c中定义 i2c bit-banging algorithm

bit-banging 是指通过设定signal pin的电平状态,由软件模拟实现串行总线的时序。若硬件已经集成总线时序的发送与接收,那么向硬件的特定寄存器写入一个字节的数据,硬件自身就可以生成该字节的时序,而bit-banging则必须由软件以bit为单位生成该字节的时序;bit-banging会增大软件开销以及CPU的负担,但可以减小硬件成本

数据结构

i2c_algo_bit_data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct i2c_algo_bit_data {
void *data; /* private data for lowlevel routines */
void (*setsda) (void *data, int state);
void (*setscl) (void *data, int state);
int (*getsda) (void *data);
int (*getscl) (void *data);
int (*pre_xfer) (struct i2c_adapter *);
void (*post_xfer) (struct i2c_adapter *);

/* local settings */
int udelay; /* half clock cycle time in us,
minimum 2 us for fast-mode I2C,
minimum 5 us for standard-mode I2C and SMBus,
maximum 50 us for SMBus */

int timeout; /* in jiffies */
};

struct i2c_algo_bit_data抽象bit-banging algorithm使用的数据,其中定义hw-specific回调函数

@data i2c adapter chip specific数据结构
@udelay i2c bus clock时钟周期的一半,以us为单位
@timeout SCL line由低电平变为高电平的过程允许的超时时间

@setsda 用于将SDA signal的电平设置为state,data参数通常为该结构的data字段
@setscl 用于将SCL signal的电平设置为state,data参数通常为该结构的data字段
@getsda 用于获取SDA signal的电平状态,data参数通常为该结构的data字段
@getscl 用于获取SCL signal的电平状态,data参数通常为该结构的data字段
@pre_xfer 用于执行数据传输前的一些准备工作,data参数通常为该结构的data字段
@post_xfer 用于执行数据传输后的一些清理工作,data参数通常为该结构的data字段

Signal Manipulation

SDA/SCL Manipulation
1
2
3
4
#define setsda(adap, val)	adap->setsda(adap->data, val)
#define setscl(adap, val) adap->setscl(adap->data, val)
#define getsda(adap) adap->getsda(adap->data)
#define getscl(adap) adap->getscl(adap->data)

getsda()/setsda() 获取/设置SDA signal的电平状态
getscl()/setscl() 获取/设置SCL signal的电平状态

1
2
3
4
5
static inline void sdalo(struct i2c_algo_bit_data *adap)
{

setsda(adap, 0);
udelay((adap->udelay + 1) / 2);
}

sdalo()拉低 SDA line,并使其低电平状态维持 1/4 周期

1
2
3
4
5
static inline void sdahi(struct i2c_algo_bit_data *adap)
{

setsda(adap, 1);
udelay((adap->udelay + 1) / 2);
}

sdahi()拉高 SDA line,并使其高电平状态维持 1/4 周期

1
2
3
4
5
static inline void scllo(struct i2c_algo_bit_data *adap)
{

setscl(adap, 0);
udelay(adap->udelay / 2);
}

scllo()拉低SCL line,并使其低电平状态维持 1/4 周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
static int sclhi(struct i2c_algo_bit_data *adap)
{

unsigned long start;

setscl(adap, 1);

/* Not all adapters have scl sense line... */
if (!adap->getscl)
goto done;

start = jiffies;
while (!getscl(adap)) {
/* This hw knows how to read the clock line, so we wait
* until it actually gets high. This is safer as some
* chips may hold it low ("clock stretching") while they
* are processing data internally.
*/

if (time_after(jiffies, start + adap->timeout)) {
/* Test one last time, as we may have been preempted
* between last check and timeout test.
*/

if (getscl(adap))
break;
return -ETIMEDOUT;
}
cpu_relax();
}
#ifdef DEBUG
if (jiffies != start && i2c_debug >= 3)
pr_debug("i2c-algo-bit: needed %ld jiffies for SCL to go "
"high\n", jiffies - start);
#endif

done:
udelay(adap->udelay);
return 0;
}

sclhi()拉高SCL line,同时若这一上升过程的时间超过i2c_algo_bit_data的timeout成员规定的允许的超时时间,则函数返回-ETIMEDOUT,否则将SCL line的高电平状态维持 1/2 时钟周期后,函数返回0

i2c_start
1
2
3
4
5
6
7
static void i2c_start(struct i2c_algo_bit_data *adap)
{

/* assert: scl, sda are high */
setsda(adap, 0);
udelay(adap->udelay);
scllo(adap);
}

i2c_start()在SCL高电平期间将SDA由高电平变为低电平,以产生开始信号S

i2c_repstart
1
2
3
4
5
6
7
8
9
static void i2c_repstart(struct i2c_algo_bit_data *adap)
{

/* assert: scl is low */
sdahi(adap);
sclhi(adap);
setsda(adap, 0);
udelay(adap->udelay);
scllo(adap);
}

i2c_repstart()将SDA、SCL恢复为高电平之后,拉低SDA以产生开始信号S

i2c_stop
1
2
3
4
5
6
7
8
static void i2c_stop(struct i2c_algo_bit_data *adap)
{

/* assert: scl is low */
sdalo(adap);
sclhi(adap);
setsda(adap, 1);
udelay(adap->udelay);
}

i2c_stop()在SCL高电平期间将SDA由低电平变为高电平,以产生结束信号P

Protocol Manipulation

i2c_inb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static int i2c_inb(struct i2c_adapter *i2c_adap)
{

/* read byte via i2c port, without start/stop sequence */
/* acknowledge is sent in i2c_read. */
int i;
unsigned char indata = 0;
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;

/* assert: scl is low */
sdahi(adap);
for (i = 0; i < 8; i++) {
if (sclhi(adap) < 0) { /* timeout */
bit_err(&i2c_adap->dev, "i2c_inb: timeout at bit "
"#%d\n", 7 - i);
return -ETIMEDOUT;
}
indata *= 2;
if (getsda(adap))
indata |= 0x01;
setscl(adap, 0);
udelay(i == 7 ? adap->udelay / 2 : adap->udelay);
}
/* assert: scl is low */
return indata;
}

i2c_outb()向i2c bus读取一个字节的数据,函数返回读取的一个字节的数据

i2c_outb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c)
{

int i;
int sb;
int ack;
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;

/* assert: scl is low */
for (i = 7; i >= 0; i--) {
sb = (c >> i) & 1;
setsda(adap, sb);
udelay((adap->udelay + 1) / 2);
if (sclhi(adap) < 0) { /* timed out */
bit_err(&i2c_adap->dev, "i2c_outb: 0x%02x, "
"timeout at bit #%d\n", (int)c, i);
return -ETIMEDOUT;
}
/* FIXME do arbitration here:
* if (sb && !getsda(adap)) -> ouch! Get out of here.
*
* Report a unique code, so higher level code can retry
* the whole (combined) message and *NOT* issue STOP.
*/

scllo(adap);
}
sdahi(adap);
if (sclhi(adap) < 0) { /* timeout */
bit_err(&i2c_adap->dev, "i2c_outb: 0x%02x, "
"timeout at ack\n", (int)c);
return -ETIMEDOUT;
}

/* read ack: SDA should be pulled down by slave, or it may
* NAK (usually to report problems with the data we wrote).
*/

ack = !getsda(adap); /* ack: sda is pulled low -> success */
bit_dbg(2, &i2c_adap->dev, "i2c_outb: 0x%02x %s\n", (int)c,
ack ? "A" : "NA");

scllo(adap);
return ack;
/* assert: scl is low (sda undef) */
}

i2c_outb()向i2c bus发送一个字节的数据,同时接收应答信号,若数据发送失败函数返回-ETIMEDOUT,否则若收到ack信号则返回1,否则返回0

bit_doAddress
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{

unsigned short flags = msg->flags;
unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;

unsigned char addr;
int ret, retries;

retries = nak_ok ? 0 : i2c_adap->retries;

if (flags & I2C_M_TEN) {
/* a ten bit address */
addr = 0xf0 | ((msg->addr >> 7) & 0x06);
bit_dbg(2, &i2c_adap->dev, "addr0: %d\n", addr);
/* try extended address code...*/
ret = try_address(i2c_adap, addr, retries);
if ((ret != 1) && !nak_ok) {
dev_err(&i2c_adap->dev,
"died at extended address code\n");
return -ENXIO;
}
/* the remaining 8 bit address */
ret = i2c_outb(i2c_adap, msg->addr & 0xff);
if ((ret != 1) && !nak_ok) {
/* the chip did not ack / xmission error occurred */
dev_err(&i2c_adap->dev, "died at 2nd address code\n");
return -ENXIO;
}
if (flags & I2C_M_RD) {
bit_dbg(3, &i2c_adap->dev, "emitting repeated "
"start condition\n");
i2c_repstart(adap);
/* okay, now switch into reading mode */
addr |= 0x01;
ret = try_address(i2c_adap, addr, retries);
if ((ret != 1) && !nak_ok) {
dev_err(&i2c_adap->dev,
"died at repeated address code\n");
return -EIO;
}
}
} else { /* normal 7bit address */
addr = msg->addr << 1;
if (flags & I2C_M_RD)
addr |= 1;
if (flags & I2C_M_REV_DIR_ADDR)
addr ^= 1;
ret = try_address(i2c_adap, addr, retries);
if ((ret != 1) && !nak_ok) {
dev_err(&i2c_adap->dev, "Error on address cycle\n");
return -ENXIO;
}
}

return 0;
}

bit_doAddress()向i2c bus发送当前i2c message对应的address byte,该字节由address[7:0]与read/write bit构成

若数据发送成功同时接收到ack信号,则函数返回0,否则函数返回负的错误码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static int try_address(struct i2c_adapter *i2c_adap,
unsigned char addr, int retries)

{

struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
int i, ret = 0;

for (i = 0; i <= retries; i++) {
ret = i2c_outb(i2c_adap, addr);
if (ret == 1 || i == retries)
break;
bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n");
i2c_stop(adap);
udelay(adap->udelay);
if (!i2c_adap->atomic_xfer_only)
cond_resched();
bit_dbg(3, &i2c_adap->dev, "emitting start condition\n");
i2c_start(adap);
}
if (i && ret)
bit_dbg(1, &i2c_adap->dev, "Used %d tries to %s client at "
"0x%02x: %s\n", i + 1,
addr & 1 ? "read from" : "write to", addr >> 1,
ret == 1 ? "success" : "failed, timeout?");
return ret;
}

try_address()向i2c bus发送 address byte,该字节由address[7:0]与read/write bit构成

addr参数为需要发送的address byte,retries参数表示重试次数,即当数据发送失败时可以再次尝试发送

若address byte发送失败则函数返回负的错误码,否则若收到ack信号则返回1,否则返回0

readbytes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{

int inval;
int rdcount = 0; /* counts bytes read */
unsigned char *temp = msg->buf;
int count = msg->len;
const unsigned flags = msg->flags;

while (count > 0) {
inval = i2c_inb(i2c_adap);
if (inval >= 0) {
*temp = inval;
rdcount++;
} else { /* read timed out */
break;
}

temp++;
count--;

/* Some SMBus transactions require that we receive the
transaction length as the first read byte. */

if (rdcount == 1 && (flags & I2C_M_RECV_LEN)) {
if (inval <= 0 || inval > I2C_SMBUS_BLOCK_MAX) {
if (!(flags & I2C_M_NO_RD_ACK))
acknak(i2c_adap, 0);
dev_err(&i2c_adap->dev, "readbytes: invalid "
"block length (%d)\n", inval);
return -EPROTO;
}
/* The original count value accounts for the extra
bytes, that is, either 1 for a regular transaction,
or 2 for a PEC transaction. */

count += inval;
msg->len += inval;
}

bit_dbg(2, &i2c_adap->dev, "readbytes: 0x%02x %s\n",
inval,
(flags & I2C_M_NO_RD_ACK)
? "(no ack/nak)"
: (count ? "A" : "NA"));

if (!(flags & I2C_M_NO_RD_ACK)) {
inval = acknak(i2c_adap, count);
if (inval < 0)
return inval;
}
}
return rdcount;
}

readbytes()向i2c bus传输一个i2c message,操作类型为读操作

函数返回成功读取的数据的字节数

sendbytes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{

const unsigned char *temp = msg->buf;
int count = msg->len;
unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
int retval;
int wrcount = 0;

while (count > 0) {
retval = i2c_outb(i2c_adap, *temp);

/* OK/ACK; or ignored NAK */
if ((retval > 0) || (nak_ok && (retval == 0)) || (count == 1)) {
count--;
temp++;
wrcount++;

/* A slave NAKing the master means the slave didn't like
* something about the data it saw. For example, maybe
* the SMBus PEC was wrong.
*/

} else if (retval == 0) {
dev_err(&i2c_adap->dev,
"sendbytes: NAK bailout on byte %d\n", count);
return -EIO;

/* Timeout; or (someday) lost arbitration
*
* FIXME Lost ARB implies retrying the transaction from
* the first message, after the "winning" master issues
* its STOP. As a rule, upper layer code has no reason
* to know or care about this ... it is *NOT* an error.
*/

} else {
dev_err(&i2c_adap->dev,
"sendbytes: error %d on byte %d\n",
retval, count);
return retval;
}
}
return wrcount;
}

sendbytes()向i2c bus传输一个i2c message,操作类型为写操作

函数返回成功写入的数据的字节数

Algorithm Core

i2c_bit_algo
1
2
3
4
const struct i2c_algorithm i2c_bit_algo = {
.master_xfer = bit_xfer,
.functionality = bit_func,
};

bit-banging algorithm 只支持standard I2C protocol,而不支持SMBus protocol

bit_func
1
2
3
4
5
6
7
static u32 bit_func(struct i2c_adapter *adap)
{

return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |
I2C_FUNC_SMBUS_READ_BLOCK_DATA |
I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
}

bit-banging algorithm的functionality()回调函数为bit_func()

bit-banging algorithm 支持standard I2C、10 bit address等功能

bit_xfer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
static int bit_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg msgs[], int num)

{

struct i2c_msg *pmsg;
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
int i, ret;
unsigned short nak_ok;

if (adap->pre_xfer) {
ret = adap->pre_xfer(i2c_adap);
if (ret < 0)
return ret;
}

bit_dbg(3, &i2c_adap->dev, "emitting start condition\n");
i2c_start(adap);
for (i = 0; i < num; i++) {
pmsg = &msgs[i];
nak_ok = pmsg->flags & I2C_M_IGNORE_NAK;
if (!(pmsg->flags & I2C_M_NOSTART)) {
if (i) {
bit_dbg(3, &i2c_adap->dev, "emitting "
"repeated start condition\n");
i2c_repstart(adap);
}
ret = bit_doAddress(i2c_adap, pmsg);
if ((ret != 0) && !nak_ok) {
bit_dbg(1, &i2c_adap->dev, "NAK from "
"device addr 0x%02x msg #%d\n",
msgs[i].addr, i);
goto bailout;
}
}
if (pmsg->flags & I2C_M_RD) {
/* read bytes into buffer*/
ret = readbytes(i2c_adap, pmsg);
if (ret >= 1)
bit_dbg(2, &i2c_adap->dev, "read %d byte%s\n",
ret, ret == 1 ? "" : "s");
if (ret < pmsg->len) {
if (ret >= 0)
ret = -EIO;
goto bailout;
}
} else {
/* write bytes from buffer */
ret = sendbytes(i2c_adap, pmsg);
if (ret >= 1)
bit_dbg(2, &i2c_adap->dev, "wrote %d byte%s\n",
ret, ret == 1 ? "" : "s");
if (ret < pmsg->len) {
if (ret >= 0)
ret = -EIO;
goto bailout;
}
}
}
ret = i;

bailout:
bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n");
i2c_stop(adap);

if (adap->post_xfer)
adap->post_xfer(i2c_adap);
return ret;
}

bit-banging algorithm的master_xfer()回调函数为bit_xfer()

  • 若i2c_algo_bit_data的pre_xfer()回调函数存在定义,则调用该回调函数执行一些数据传输前的准备工作
  • 调用i2c_start()产生开始信号S
  • 传输i2c message,对于需要传输的每个i2c message
    • 调用bit_doAddress()发送address byte
    • 若当前i2c message为读操作,则调用readbytes()读取数据并保存在i2c message中,否则调用sendbytes()发送数据
  • 调用i2c_stop()产生结束信号P
  • 若i2c_algo_bit_data的post_xfer()回调函数存在定义,则调用该回调函数执行一些数据传输后的清理工作
i2c_bit_add_bus
1
2
3
4
int i2c_bit_add_bus(struct i2c_adapter *adap)
{

return __i2c_bit_add_bus(adap, i2c_add_adapter);
}

i2c_bit_add_bus()注册一个使用bit-banging algorithm的i2c adapter,该adapetr的bus ID由i2c core动态分配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
static int __i2c_bit_add_bus(struct i2c_adapter *adap,
int (*add_adapter)(struct i2c_adapter *))
{
struct i2c_algo_bit_data *bit_adap = adap->algo_data;
int ret;

if (bit_test) {
ret = test_bus(adap);
if (bit_test >= 2 && ret < 0)
return -ENODEV;
}

/* register new adapter to i2c module... */
adap->algo = &i2c_bit_algo;
adap->retries = 3;

ret = add_adapter(adap);
if (ret < 0)
return ret;

/* Complain if SCL can't be read */
if (bit_adap->getscl == NULL) {
dev_warn(&adap->dev, "Not I2C compliant: can't read SCL\n");
dev_warn(&adap->dev, "Bus may be unreliable\n");
}
return 0;
}
i2c_bit_add_numbered_bus
1
2
3
4
int i2c_bit_add_numbered_bus(struct i2c_adapter *adap)
{

return __i2c_bit_add_bus(adap, i2c_add_numbered_adapter);
}

i2c_bit_add_bus()注册一个使用bit-banging algorithm的i2c adapter,该adapetr的bus ID由该adapter的nr成员指定

I2C Mux

当系统中存在一个I2C controller以及一个I2C multiplexer时,I2C Multiplexing允许将一个I2C controller划分为多个multiplexed segment,每个segment都可以视为一个independent i2c adapter

例如下图中存在i2c-0 adapter、i2c-1 adapter以及i2c-2 adapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
+------------+   segment 0 (i2c-0)
| controller |-------+--------+----------+---
+------------+ | | |
| | |
| +-------+ +-------+
| | dev A | | dev B |
| +-------+ +-------+
|
| +-------+ +-------+
| | dev C | | dev D |
| +-------+ +-------+
| | |
+-----+ segment 1 (i2c-1) | |
| |---------------------+--- |
| MUX | |
| |--------------------------------+---
+-----+ segment 2 (i2c-2)

include/linux/i2c-mux.hdrivers/i2c/i2c-mux.c中定义 I2C Mux 的数据结构与操作函数
drivers/i2c/mux/下为i2c multiplexer chip driver

数据结构

i2c_mux_priv
1
2
3
4
5
6
7
8
9
10
11
struct i2c_mux_priv {
struct i2c_adapter adap;
struct i2c_algorithm algo;

struct i2c_adapter *parent;
void *mux_priv; /* the mux chip/device */
u32 chan_id; /* the channel id */

int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id);
int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id);
};

struct i2c_mux_priv抽象i2c mux segment,即为每个i2c mux channel维护一个i2c_mux_priv结构

@adap 该i2c mux segment对应的i2c adapter,每个i2c mux segment都可以视为一个independent i2c adapter
@algo 该i2c mux segment对应的i2c adapter的algorithm
@parent 与该i2c multiplexer相连接的i2c controller
@mux_priv 该i2c mux channel所属的i2c multiplexer
@chan_id 该i2c mux channel ID
@select 用于特定i2c mux channel path的导通
@deselect 用于特定i2c mux channel path的关闭

I2C mux core

i2c_add_mux_adapter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
struct device *mux_dev,
void *mux_priv, u32 force_nr, u32 chan_id,
unsigned int class,
int (*select) (struct i2c_adapter *,
void *, u32),
int (*deselect) (struct i2c_adapter *,
void *, u32))
{
struct i2c_mux_priv *priv;
int ret;

priv = kzalloc(sizeof(struct i2c_mux_priv), GFP_KERNEL);
if (!priv)
return NULL;

/* Set up private adapter data */
priv->parent = parent;
priv->mux_priv = mux_priv;
priv->chan_id = chan_id;
priv->select = select;

/* Need to do algo dynamically because we don't know ahead
* of time what sort of physical adapter we'll be dealing with.
*/
if (parent->algo->master_xfer)
priv->algo.master_xfer = i2c_mux_master_xfer;
if (parent->algo->smbus_xfer)
priv->algo.smbus_xfer = i2c_mux_smbus_xfer;
priv->algo.functionality = i2c_mux_functionality;

/* Now fill out new adapter structure */
snprintf(priv->adap.name, sizeof(priv->adap.name),
"i2c-%d-mux (chan_id %d)", i2c_adapter_id(parent), chan_id);
priv->adap.owner = THIS_MODULE;
priv->adap.algo = &priv->algo;
priv->adap.algo_data = priv;
priv->adap.dev.parent = &parent->dev;

/* Sanity check on class */
if (i2c_mux_parent_classes(parent) & class)
dev_err(&parent->dev,
"Segment %d behind mux can't share classes with ancestors\n",
chan_id);
else
priv->adap.class = class;

/*
* Try to populate the mux adapter's of_node, expands to
* nothing if !CONFIG_OF.
*/
if (mux_dev->of_node) {
struct device_node *child;
bool enable_deselect;
u32 reg;

for_each_child_of_node(mux_dev->of_node, child) {
ret = of_property_read_u32(child, "reg", &reg);
if (ret)
continue;
if (chan_id == reg) {
priv->adap.dev.of_node = child;
enable_deselect = of_property_read_bool(child,
"i2c-mux,deselect-on-exit");
if (enable_deselect)
priv->deselect = deselect;
break;
}
}
} else {
priv->deselect = deselect;
}

if (force_nr) {
priv->adap.nr = force_nr;
ret = i2c_add_numbered_adapter(&priv->adap);
} else {
ret = i2c_add_adapter(&priv->adap);
}
if (ret < 0) {
dev_err(&parent->dev,
"failed to add mux-adapter (error=%d)\n",
ret);
kfree(priv);
return NULL;
}

dev_info(&parent->dev, "Added multiplexed i2c bus %d\n",
i2c_adapter_id(&priv->adap));

of_i2c_register_devices(&priv->adap);

return &priv->adap;
}

i2c_add_mux_adapter()注册该i2c mux channel对应的i2c adapter

@parent 与该i2c multiplexer相连接的i2c controller
@mux_dev 该i2c mux channel所属的i2c multiplexer的device结构
@mux_priv 该i2c mux channel所属的i2c multiplexer
@force_nr 指定该i2c mux channel对应的i2c adapter的bus number,若该参数为0则为该i2c adapter动态分配bus number
@chan_id 该i2c mux channel ID
@class 该i2c mux channel对应的i2c adapter的class,描述其对应的i2c adapter支持的slave device的类型
@select/deselect

成功返回该i2c mux channel对应的i2c adapter,否则返回NULL

  • 根据传入的参数创建并初始化该i2c mux channel对应的i2c_mux_priv结构
  • 调用i2c_add_adapter()或i2c_add_numbered_adapter()注册该i2c mux channel对应的i2c adapter
  • 调用of_i2c_register_devices()注册在设备树中定义的该i2c mux channel下的所有i2c client
i2c_del_mux_adapter
1
2
3
4
5
6
7
void i2c_del_mux_adapter(struct i2c_adapter *adap)
{

struct i2c_mux_priv *priv = adap->algo_data;

i2c_del_adapter(adap);
kfree(priv);
}

i2c_del_mux_adapter()注销i2c mux channel对应的i2c adapter

i2c_mux_functionality
1
2
3
4
5
6
7
static u32 i2c_mux_functionality(struct i2c_adapter *adap)
{

struct i2c_mux_priv *priv = adap->algo_data;
struct i2c_adapter *parent = priv->parent;

return parent->algo->functionality(parent);
}

i2c mux channel对应的i2c adapter的algorithm的functionality()回调函数为i2c_mux_functionality(),该函数实际调用该i2c multiplexer连接的i2c controller对应的i2c adapter的functionality()回调函数

i2c_mux_master_xfer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static int i2c_mux_master_xfer(struct i2c_adapter *adap,
struct i2c_msg msgs[], int num)

{

struct i2c_mux_priv *priv = adap->algo_data;
struct i2c_adapter *parent = priv->parent;
int ret;

/* Switch to the right mux port and perform the transfer. */

ret = priv->select(parent, priv->mux_priv, priv->chan_id);
if (ret >= 0)
ret = parent->algo->master_xfer(parent, msgs, num);
if (priv->deselect)
priv->deselect(parent, priv->mux_priv, priv->chan_id);

return ret;
}

当parent i2c controller对应的i2c adapter的master_xfer()回调函数存在定义时,i2c mux channel对应的i2c adapter的master_xfer()回调函数为i2c_mux_master_xfer()

  • 调用该i2c mux channel的select()回调函数实现该i2c mux channel path的导通
  • 调用parent i2c controller对应的i2c adapter的master_xfer()回调函数实现真正的数据传输
  • 若该i2c mux channel的deselect()回调函数存在定义,则调用该回调函数实现该i2c mux channel path的关闭
i2c_mux_smbus_xfer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static int i2c_mux_smbus_xfer(struct i2c_adapter *adap,
u16 addr, unsigned short flags,
char read_write, u8 command,
int size, union i2c_smbus_data *data)

{

struct i2c_mux_priv *priv = adap->algo_data;
struct i2c_adapter *parent = priv->parent;
int ret;

/* Select the right mux port and perform the transfer. */

ret = priv->select(parent, priv->mux_priv, priv->chan_id);
if (ret >= 0)
ret = parent->algo->smbus_xfer(parent, addr, flags,
read_write, command, size, data);
if (priv->deselect)
priv->deselect(parent, priv->mux_priv, priv->chan_id);

return ret;
}

当parent i2c controller对应的i2c adapter的smbus_xfer()回调函数存在定义时,i2c mux channel对应的i2c adapter的master_xfer()回调函数为i2c_mux_smbus_xfer()

  • 调用该i2c mux channel的select()回调函数实现该i2c mux channel path的导通
  • 调用parent i2c controller对应的i2c adapter的smbus_xfer()回调函数实现真正的数据传输
  • 若该i2c mux channel的deselect()回调函数存在定义,则调用该回调函数实现该i2c mux channel path的关闭

i2cdev

include/linux/i2c-dev.hinclude/uapi/linux/i2c-dev.hdrivers/i2c/i2c-dev.c中定义i2cdev

通常用户态程序通过i2c protocol driver即i2c client driver实现与特定i2c client的数据传输,i2cdev core提供I2C bus interface,通过i2c adapter的设备的设备节点实现用户态程序与该i2c adapter下的特定i2c device的数据传输

数据结构

i2c_dev
1
2
3
4
5
struct i2c_dev {
struct list_head list;
struct i2c_adapter *adap;
struct device *dev;
};

struct i2c_dev抽象i2cdev,每个i2cdev对应一个i2c adapter的设备文件

@list 通过该链表头将该i2cdev添加到i2c_dev_list全局链表中
@adap 该i2cdev对应的i2c adapter
@dev 用于创建该i2cdev对应的设备节点所需的device结构

i2c_rdwr_ioctl_data
1
2
3
4
struct i2c_rdwr_ioctl_data {
struct i2c_msg __user *msgs; /* pointers to i2c_msgs */
__u32 nmsgs; /* number of i2c_msgs */
};

struct i2c_rdwr_ioctl_data抽象i2cdev中I2C protocol数据传输的相关数据结构

@msgs 需要传输的i2c message数组,其中i2c message中传输的数据的大小限制为8192个字节
@nmsgs 需要传输的i2c message的数量,最大为I2C_RDRW_IOCTL_MAX_MSGS,即42

1
#define  I2C_RDRW_IOCTL_MAX_MSGS    42
i2c_smbus_ioctl_data
1
2
3
4
5
6
struct i2c_smbus_ioctl_data {
__u8 read_write;
__u8 command;
__u32 size;
union i2c_smbus_data __user *data;
};

struct i2c_smbus_ioctl_data抽象i2cdev中SMBus protocol数据传输的相关数据结构

@read_write 数据发送或接收的标志位,I2C_SMBUS_READ or I2C_SMBUS_WRITE
@command 通常表示slave device需要读取或写入的寄存器地址
@size 通常表示传输的数据的大小,即需要执行的SMBus protocol operation的类型,如I2C_SMBUS_BYTE_DATA等
@data 需要传输的数据,SMBus protocol数据传输以i2c_smbus_data为单位

device number
1
2
#define I2C_MAJOR	89		/* Device major number */
#define I2C_MINORS 256

创建的设备节点对应的cdev的主设备号为89,从设备号为i2c adapter的bus number,共支持256个从设备号

i2c_dev_list 链表
1
static LIST_HEAD(i2c_dev_list);

i2cdev core定义i2c_dev_list全局链表,用于管理所有的i2cdev

i2c_dev_class
1
static struct class *i2c_dev_class;

当使用mdev/udev创建相应的设备节点时,必须调用device_create()/device_add(),而同时device_create()/device_add()需要传入device的class参数,因而必须创建相应的class,i2cdev中定义i2cdev class

i2cdev core init

i2c_dev_init
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
static int __init i2c_dev_init(void)
{

int res;

printk(KERN_INFO "i2c /dev entries driver\n");

res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
if (res)
goto out;

i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
if (IS_ERR(i2c_dev_class)) {
res = PTR_ERR(i2c_dev_class);
goto out_unreg_chrdev;
}

/* Keep track of adapters which will be added or removed later */
res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
if (res)
goto out_unreg_class;

/* Bind to already existing adapters right away */
i2c_for_each_dev(NULL, i2cdev_attach_adapter);

return 0;

out_unreg_class:
class_destroy(i2c_dev_class);
out_unreg_chrdev:
unregister_chrdev(I2C_MAJOR, "i2c");
out:
printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
return res;
}

i2c_dev_init()完成i2cdev core的初始化

  • 注册i2cdev对应的cdev,其名称为”i2c”,主设备号为I2C_MAJOR,对应的file_operations结构为i2cdev_fops
  • 注册i2cdev对应的device class,名称为”i2c-dev”
  • 调用bus_register_notifier()向i2c bus注册i2cdev_notifier
  • 遍历当前注册的所有i2c adapter,对每个i2c adapter调用i2cdev_attach_adapter()以实现i2cdev与对应的i2c adapter的binding
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{

struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
int res;

if (dev->type != &i2c_adapter_type)
return 0;
adap = to_i2c_adapter(dev);

i2c_dev = get_free_i2c_dev(adap);
if (IS_ERR(i2c_dev))
return PTR_ERR(i2c_dev);

/* register this i2c device with the driver core */
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
MKDEV(I2C_MAJOR, adap->nr), NULL,
"i2c-%d", adap->nr);
if (IS_ERR(i2c_dev->dev)) {
res = PTR_ERR(i2c_dev->dev);
goto error;
}
res = device_create_file(i2c_dev->dev, &dev_attr_name);
if (res)
goto error_destroy;

pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
adap->name, adap->nr);
return 0;
error_destroy:
device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
error:
return_i2c_dev(i2c_dev);
return res;
}

i2cdev_attach_adapter()以实现i2cdev与对应的i2c adapter的binding

  • 对于当前遍历的i2c adapter,调用get_free_i2c_dev()创建并初始化该i2c adapter对应的i2cdev
  • 调用device_create()创建该i2cdev的device结构,该device的class为i2c_dev_class,其parent device为对应的i2c adapter的device结构,device的driver data为该i2c adapter的bus number,device的名称为”i2c-
  • 若对应的device创建成功,则在该device对应的sysfs目录下创建dev_attr_name属性文件,即name属性文件,显示该i2c adapter的名称
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap)
{

struct i2c_dev *i2c_dev;

if (adap->nr >= I2C_MINORS) {
printk(KERN_ERR "i2c-dev: Out of device minors (%d)\n",
adap->nr);
return ERR_PTR(-ENODEV);
}

i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL);
if (!i2c_dev)
return ERR_PTR(-ENOMEM);
i2c_dev->adap = adap;

spin_lock(&i2c_dev_list_lock);
list_add_tail(&i2c_dev->list, &i2c_dev_list);
spin_unlock(&i2c_dev_list_lock);
return i2c_dev;
}

get_free_i2c_dev()创建并初始化该i2c adapter对应的i2cdev,设置其adap成员为传入的i2c adapter,并将创建的i2cdev添加到i2c_dev_list全局链表中

当向i2c bus添加或移除i2c adapter时,都将调用i2cdev_notifier的notifier_call()回调函数,即i2cdev_notifier_call()以实现i2c adapter与i2cdev的binding/unbinding

i2cdev_notifier
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static struct notifier_block i2cdev_notifier = {
.notifier_call = i2cdev_notifier_call,
};

static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,
void *data)

{

struct device *dev = data;

switch (action) {
case BUS_NOTIFY_ADD_DEVICE:
return i2cdev_attach_adapter(dev, NULL);
case BUS_NOTIFY_DEL_DEVICE:
return i2cdev_detach_adapter(dev, NULL);
}

return 0;
}

i2cdev core

i2cdev_fops
1
2
3
4
5
6
7
8
9
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.unlocked_ioctl = i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};

i2cdev对应的cdev的file_operations结构为i2cdev_fops

i2cdev_open
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
static int i2cdev_open(struct inode *inode, struct file *file)
{

unsigned int minor = iminor(inode);
struct i2c_client *client;
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;

i2c_dev = i2c_dev_get_by_minor(minor);
if (!i2c_dev)
return -ENODEV;

adap = i2c_get_adapter(i2c_dev->adap->nr);
if (!adap)
return -ENODEV;

/* This creates an anonymous i2c_client, which may later be
* pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.
*
* This client is ** NEVER REGISTERED ** with the driver model
* or I2C core code!! It just holds private copies of addressing
* information and maybe a PEC flag.
*/

client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client) {
i2c_put_adapter(adap);
return -ENOMEM;
}
snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);

client->adapter = adap;
file->private_data = client;

return 0;
}

当对设备节点进行open操作时,实际调用i2cdev_open()

  • 当打开设备文件时,inode->i_rdev记录了当前打开的设备节点对应的设备号,通过当前的设备号调用i2c_dev_get_by_minor()获取当前打开的设备文件对应的i2cdev
  • 调用i2c_get_adapter()增加当前的i2cdev对应的i2c adapter的引用计数
  • 创建一个anonymous i2c client,在之后的操作中该i2c client将被用于数据传输相关的操作
  • 设置创建的i2c client的adapter成员为当前的i2c adapter,并将该i2c client赋值给file->private_data
i2cdev_read
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
loff_t *offset)

{

char *tmp;
int ret;

struct i2c_client *client = file->private_data;

if (count > 8192)
count = 8192;

tmp = kmalloc(count, GFP_KERNEL);
if (tmp == NULL)
return -ENOMEM;

pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
iminor(file_inode(file)), count);

ret = i2c_master_recv(client, tmp, count);
if (ret >= 0)
ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;
kfree(tmp);
return ret;
}

i2cdev core主要通过设备节点的ioctrl操作导出i2c bus interface,通过ioctrl(I2C_RDWR,…)可以直接使用对应的i2c adapter传输特定i2c message,由于i2c message中包含target i2c device的地址信息,因而可以实现与特定i2c device的数据传输

然而当该controller不支持i2c protocol而只支持SMBus protocol,需要通过SMBus protocol进行数据传输,或需要通过read()/write()系统调用进行数据传输时,必须首先调用ioctrl(I2C_SLAVE/I2C_SLAVE_FORCE,…)在anonymous i2c client中设置相应的地址信息,之后通过read()/write()系统调用,或SMBus interface进行数据传输

当对设备节点进行读操作时,实际调用i2cdev_read()从特定i2c client接受i2c message,此时anonymous i2c client中已经保存target i2c client的地址信息;buf参数用于保存接收的数据,count参数表示需要接收的数据的长度,以字节为单位

  • 调用i2c_master_recv()接收i2c message,一共接收count字节的数据
  • 将接收的数据拷贝到传入的用户态的buf缓存中
i2cdev_write
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static ssize_t i2cdev_write(struct file *file, const char __user *buf,
size_t count, loff_t *offset)

{

int ret;
char *tmp;
struct i2c_client *client = file->private_data;

if (count > 8192)
count = 8192;

tmp = memdup_user(buf, count);
if (IS_ERR(tmp))
return PTR_ERR(tmp);

pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n",
iminor(file_inode(file)), count);

ret = i2c_master_send(client, tmp, count);
kfree(tmp);
return ret;
}

当对设备节点进行写操作时,实际调用i2cdev_write()向特定i2c client发送i2c message,此时anonymous i2c client中已经保存target i2c client的地址信息;buf参数指向的缓存中保存有需要发送的的数据,count参数表示需要发送的数据的长度,以字节为单位

  • 将传入的buf缓存中保存的需要发送的数据拷贝到内核态缓存中
  • 调用i2c_master_send()发送i2c message,一共发送count字节的数据
i2cdev_ioctl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct i2c_client *client = file->private_data;
unsigned long funcs;

dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
cmd, arg);

switch (cmd) {
case I2C_SLAVE:
case I2C_SLAVE_FORCE:
/* NOTE: devices set up to work with "new style" drivers
* can't use I2C_SLAVE, even when the device node is not
* bound to a driver. Only I2C_SLAVE_FORCE will work.
*
* Setting the PEC flag here won't affect kernel drivers,
* which will be using the i2c_client node registered with
* the driver model core. Likewise, when that client has
* the PEC flag already set, the i2c-dev driver won't see
* (or use) this setting.
*/
if ((arg > 0x3ff) ||
(((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
return -EINVAL;
if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))
return -EBUSY;
/* REVISIT: address could become busy later */
client->addr = arg;
return 0;
case I2C_TENBIT:
if (arg)
client->flags |= I2C_M_TEN;
else
client->flags &= ~I2C_M_TEN;
return 0;
case I2C_PEC:
if (arg)
client->flags |= I2C_CLIENT_PEC;
else
client->flags &= ~I2C_CLIENT_PEC;
return 0;
case I2C_FUNCS:
funcs = i2c_get_functionality(client->adapter);
return put_user(funcs, (unsigned long __user *)arg);

case I2C_RDWR:
return i2cdev_ioctl_rdrw(client, arg);

case I2C_SMBUS:
return i2cdev_ioctl_smbus(client, arg);

case I2C_RETRIES:
client->adapter->retries = arg;
break;
case I2C_TIMEOUT:
/* For historical reasons, user-space sets the timeout
* value in units of 10 ms.
*/
client->adapter->timeout = msecs_to_jiffies(arg * 10);
break;
default:
/* NOTE: returning a fault code here could cause trouble
* in buggy userspace code. Some old kernel bugs returned
* zero in this case, and userspace code might accidentally
* have depended on that bug.
*/
return -ENOTTY;
}
return 0;
}

i2cdev_ioctl()导出i2c bus interface,包括

ioctl(file, I2C_SLAVE, long addr)
ioctl(file, I2C_SI2C_SLAVE_FORCELAVE, long addr)

I2C_SLAVE/I2C_SLAVE_FORCE用于设定anonymous i2c client的地址,传入的addr参数为需要设置的i2c地址,当对应地址处的i2c device已经与相应的i2c driver实现绑定时,必须使用I2C_SLAVE_FORCE设置anonymous i2c client的地址

ioctl(file, I2C_TENBIT, long select)

I2C_TENBIT用于设置或清除anonymous i2c client的I2C_M_TEN标志,若传入的参数select为1则设置I2C_M_TEN标志,否则清除I2C_M_TEN标志

ioctl(file, I2C_PEC, long select)

I2C_PEC用于设置或清除anonymous i2c client的I2C_CLIENT_PEC标志,若传入的参数select为1则设置I2C_CLIENT_PEC标志,否则清除I2C_CLIENT_PEC标志

ioctl(file, I2C_FUNCS, unsigned long *funcs)

I2C_FUNCS用于返回当前设备节点对应的i2c adapter支持的所有functionality,并保存在传入的funcs参数指向的缓存中

ioctl(file, I2C_RDWR, struct i2c_rdwr_ioctl_data *msgset)

I2C_RDWR用于实现I2C protocol数据传输,传输一个或多个i2c message

ioctl(file, I2C_SMBUS, struct i2c_smbus_ioctl_data *args)

I2C_SMBUS用于实现SMBus protocol数据传输,在进行SMBus protocol数据传输之前,必须首先调用I2C_SLAVE/I2C_SLAVE_FORCE ioctl设定anonymous i2c client的地址

ioctl(file, I2C_RETRIES, long retries)

I2C_RETRIES用于设置对应的i2c adapter的retries参数,传入的retries参数即为需要设置的重试次数

ioctl(file, I2C_TIMEOUT, long timeout_in_10ms)

I2C_RETRIES用于设置对应的i2c adapter的timeout参数,传入的timeout_in_10ms即为需要设置的超时时间,单位为10ms

i2cdev_ioctl_rdrw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,
unsigned long arg)
{
struct i2c_rdwr_ioctl_data rdwr_arg;
struct i2c_msg *rdwr_pa;
u8 __user **data_ptrs;
int i, res;

if (copy_from_user(&rdwr_arg,
(struct i2c_rdwr_ioctl_data __user *)arg,
sizeof(rdwr_arg)))
return -EFAULT;

/* Put an arbitrary limit on the number of messages that can
* be sent at once */
if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)
return -EINVAL;

rdwr_pa = memdup_user(rdwr_arg.msgs,
rdwr_arg.nmsgs * sizeof(struct i2c_msg));
if (IS_ERR(rdwr_pa))
return PTR_ERR(rdwr_pa);

data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);
if (data_ptrs == NULL) {
kfree(rdwr_pa);
return -ENOMEM;
}

res = 0;
for (i = 0; i < rdwr_arg.nmsgs; i++) {
/* Limit the size of the message to a sane amount */
if (rdwr_pa[i].len > 8192) {
res = -EINVAL;
break;
}

data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;
rdwr_pa[i].buf = memdup_user(data_ptrs[i], rdwr_pa[i].len);
if (IS_ERR(rdwr_pa[i].buf)) {
res = PTR_ERR(rdwr_pa[i].buf);
break;
}

/*
* If the message length is received from the slave (similar
* to SMBus block read), we must ensure that the buffer will
* be large enough to cope with a message length of
* I2C_SMBUS_BLOCK_MAX as this is the maximum underlying bus
* drivers allow. The first byte in the buffer must be
* pre-filled with the number of extra bytes, which must be
* at least one to hold the message length, but can be
* greater (for example to account for a checksum byte at
* the end of the message.)
*/
if (rdwr_pa[i].flags & I2C_M_RECV_LEN) {
if (!(rdwr_pa[i].flags & I2C_M_RD) ||
rdwr_pa[i].buf[0] < 1 ||
rdwr_pa[i].len < rdwr_pa[i].buf[0] +
I2C_SMBUS_BLOCK_MAX) {
res = -EINVAL;
break;
}

rdwr_pa[i].len = rdwr_pa[i].buf[0];
}
}
if (res < 0) {
int j;
for (j = 0; j < i; ++j)
kfree(rdwr_pa[j].buf);
kfree(data_ptrs);
kfree(rdwr_pa);
return res;
}

res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);
while (i-- > 0) {
if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) {
if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf,
rdwr_pa[i].len))
res = -EFAULT;
}
kfree(rdwr_pa[i].buf);
}
kfree(data_ptrs);
kfree(rdwr_pa);
return res;
}

i2cdev_ioctl_rdrw()用于实现i2cdev中的I2C protocol数据传输,传入的arg参数实际为i2c_rdwr_ioctl_data结构,其中保存需要传输的一个或多个i2c message

  • 实际调用i2c_transfer()对i2c_rdwr_ioctl_data中包含的一个或多个i2c message进行传输
i2cdev_ioctl_smbus
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
static noinline int i2cdev_ioctl_smbus(struct i2c_client *client,
unsigned long arg)

{

struct i2c_smbus_ioctl_data data_arg;
union i2c_smbus_data temp;
int datasize, res;

if (copy_from_user(&data_arg,
(struct i2c_smbus_ioctl_data __user *) arg,
sizeof(struct i2c_smbus_ioctl_data)))
return -EFAULT;
if ((data_arg.size != I2C_SMBUS_BYTE) &&
(data_arg.size != I2C_SMBUS_QUICK) &&
(data_arg.size != I2C_SMBUS_BYTE_DATA) &&
(data_arg.size != I2C_SMBUS_WORD_DATA) &&
(data_arg.size != I2C_SMBUS_PROC_CALL) &&
(data_arg.size != I2C_SMBUS_BLOCK_DATA) &&
(data_arg.size != I2C_SMBUS_I2C_BLOCK_BROKEN) &&
(data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA) &&
(data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) {
dev_dbg(&client->adapter->dev,
"size out of range (%x) in ioctl I2C_SMBUS.\n",
data_arg.size);
return -EINVAL;
}
/* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1,
so the check is valid if size==I2C_SMBUS_QUICK too. */

if ((data_arg.read_write != I2C_SMBUS_READ) &&
(data_arg.read_write != I2C_SMBUS_WRITE)) {
dev_dbg(&client->adapter->dev,
"read_write out of range (%x) in ioctl I2C_SMBUS.\n",
data_arg.read_write);
return -EINVAL;
}

/* Note that command values are always valid! */

if ((data_arg.size == I2C_SMBUS_QUICK) ||
((data_arg.size == I2C_SMBUS_BYTE) &&
(data_arg.read_write == I2C_SMBUS_WRITE)))
/* These are special: we do not use data */
return i2c_smbus_xfer(client->adapter, client->addr,
client->flags, data_arg.read_write,
data_arg.command, data_arg.size, NULL);

if (data_arg.data == NULL) {
dev_dbg(&client->adapter->dev,
"data is NULL pointer in ioctl I2C_SMBUS.\n");
return -EINVAL;
}

if ((data_arg.size == I2C_SMBUS_BYTE_DATA) ||
(data_arg.size == I2C_SMBUS_BYTE))
datasize = sizeof(data_arg.data->byte);
else if ((data_arg.size == I2C_SMBUS_WORD_DATA) ||
(data_arg.size == I2C_SMBUS_PROC_CALL))
datasize = sizeof(data_arg.data->word);
else /* size == smbus block, i2c block, or block proc. call */
datasize = sizeof(data_arg.data->block);

if ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
(data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||
(data_arg.size == I2C_SMBUS_I2C_BLOCK_DATA) ||
(data_arg.read_write == I2C_SMBUS_WRITE)) {
if (copy_from_user(&temp, data_arg.data, datasize))
return -EFAULT;
}
if (data_arg.size == I2C_SMBUS_I2C_BLOCK_BROKEN) {
/* Convert old I2C block commands to the new
convention. This preserves binary compatibility. */

data_arg.size = I2C_SMBUS_I2C_BLOCK_DATA;
if (data_arg.read_write == I2C_SMBUS_READ)
temp.block[0] = I2C_SMBUS_BLOCK_MAX;
}
res = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
data_arg.read_write, data_arg.command, data_arg.size, &temp);
if (!res && ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
(data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||
(data_arg.read_write == I2C_SMBUS_READ))) {
if (copy_to_user(data_arg.data, &temp, datasize))
return -EFAULT;
}
return res;
}

i2cdev_ioctl_smbus()用于实现i2cdev中的SMBus protocol数据传输,传入的arg参数实际为i2c_smbus_ioctl_data结构

  • 实际调用i2c_smbus_xfer()对i2c_smbus_ioctl_data中包含的数据进行传输
i2cdev SMBus userspace interface

通常不直接调用ioctrl(I2C_SMBUS,…)进行SMBus protocol数据传输,而是调用用户态的include/linux/i2c-dev.h中定义的一系列接口进行SMBus protocol数据传输

i2c_smbus_access
1
2
3
4
5
6
7
8
9
10
11
static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, 
int size, union i2c_smbus_data *data)

{

struct i2c_smbus_ioctl_data args;

args.read_write = read_write;
args.command = command;
args.size = size;
args.data = data;
return ioctl(file,I2C_SMBUS,&args);
}

i2c_smbus_access()通过ioctrl(I2C_SMBUS,…)对特定i2c client进行SMBus protocol数据传输,在此之前必须调用ioctrl(I2C_SLAVE,…)设定target i2c client的地址

成功返回0,否则返回-1

@file 打开的i2c adapter的设备节点的文件描述符
@read_write 对应于i2c_smbus_ioctl_data.read_write
@comand 对应于i2c_smbus_ioctl_data.command
@size 对应于i2c_smbus_ioctl_data.size
@data 对应于i2c_smbus_ioctl_data.data

i2c_smbus_write_quick
1
2
3
4
static inline __s32 i2c_smbus_write_quick(int file, __u8 value)
{

return i2c_smbus_access(file,value,0,I2C_SMBUS_QUICK,NULL);
}
i2c_smbus_read_byte
1
2
3
4
5
6
7
8
static inline __s32 i2c_smbus_read_byte(int file)
{

union i2c_smbus_data data;
if (i2c_smbus_access(file,I2C_SMBUS_READ,0,I2C_SMBUS_BYTE,&data))
return -1;
else
return 0x0FF & data.byte;
}
i2c_smbus_write_byte
1
2
3
4
5
static inline __s32 i2c_smbus_write_byte(int file, __u8 value)
{

return i2c_smbus_access(file,I2C_SMBUS_WRITE,value,
I2C_SMBUS_BYTE,NULL);
}
i2c_smbus_read_byte_data
1
2
3
4
5
6
7
8
9
static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command)
{

union i2c_smbus_data data;
if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
I2C_SMBUS_BYTE_DATA,&data))
return -1;
else
return 0x0FF & data.byte;
}
i2c_smbus_write_byte_data
1
2
3
4
5
6
7
8
static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, 
__u8 value)

{

union i2c_smbus_data data;
data.byte = value;
return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
I2C_SMBUS_BYTE_DATA, &data);
}
i2c_smbus_read_word_data
1
2
3
4
5
6
7
8
9
static inline __s32 i2c_smbus_read_word_data(int file, __u8 command)
{

union i2c_smbus_data data;
if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
I2C_SMBUS_WORD_DATA,&data))
return -1;
else
return 0x0FFFF & data.word;
}
i2c_smbus_write_word_data
1
2
3
4
5
6
7
8
static inline __s32 i2c_smbus_write_word_data(int file, __u8 command, 
__u16 value)

{

union i2c_smbus_data data;
data.word = value;
return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
I2C_SMBUS_WORD_DATA, &data);
}
i2c_smbus_process_call
1
2
3
4
5
6
7
8
9
10
static inline __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value)
{

union i2c_smbus_data data;
data.word = value;
if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
I2C_SMBUS_PROC_CALL,&data))
return -1;
else
return 0x0FFFF & data.word;
}
i2c_smbus_read_block_data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Returns the number of read bytes */
static inline __s32 i2c_smbus_read_block_data(int file, __u8 command,
__u8 *values)

{

union i2c_smbus_data data;
int i;
if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
I2C_SMBUS_BLOCK_DATA,&data))
return -1;
else {
for (i = 1; i <= data.block[0]; i++)
values[i-1] = data.block[i];
return data.block[0];
}
}
i2c_smbus_write_block_data
1
2
3
4
5
6
7
8
9
10
11
12
13
static inline __s32 i2c_smbus_write_block_data(int file, __u8 command, 
__u8 length, __u8 *values)

{

union i2c_smbus_data data;
int i;
if (length > 32)
length = 32;
for (i = 1; i <= length; i++)
data.block[i] = values[i-1];
data.block[0] = length;
return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
I2C_SMBUS_BLOCK_DATA, &data);
}
i2c_smbus_write_i2c_block_data
1
2
3
4
5
6
7
8
9
10
11
12
13
static inline __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command,
__u8 length, __u8 *values)

{

union i2c_smbus_data data;
int i;
if (length > 32)
length = 32;
for (i = 1; i <= length; i++)
data.block[i] = values[i-1];
data.block[0] = length;
return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
I2C_SMBUS_I2C_BLOCK_DATA, &data);
}