目录

目录

​​数据收发流程​​

​​驱动层 收发接口​​

​​驱动层注册​​

​​smbus控制器​​

​​i2c控制器 ​​

​​协议接口使用​​

​​总体流程​​

​​数据收发关键数据结构​​

​​使用示例--eeprom的读写​​

​​使用示例--smbus的读写接口​​


数据收发流程

驱动层 收发接口

首先了解,驱动层向协议(算法)层注册的接口。

注释写的清楚:

1)如果不支持I2C 层级的访问,则设置i2c收发接口master_xfer为空,否则设置I2C控制器的接口;

2)如果控制器支持smbus协议,则对应的驱动设置收发接口smbus_xfer.

struct i2c_algorithm {
/*
* If an adapter algorithm can't do I2C-level access, set master_xfer
* to NULL. If an adapter algorithm can do SMBus access, set
* smbus_xfer. If set to NULL, the SMBus protocol is simulated
* using common I2C messages.
*
* master_xfer should return the number of messages successfully
* processed, or a negative value on error
*/
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
int (*master_xfer_atomic)(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);
int (*smbus_xfer_atomic)(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 *adap);

#if IS_ENABLED(CONFIG_I2C_SLAVE)
int (*reg_slave)(struct i2c_client *client);
int (*unreg_slave)(struct i2c_client *client);
#endif
};

驱动层注册

对于此数据结构的使用,内核i801(intel smbus控制器)和 imx i2c 驱动,给出了示例:

smbus控制器

static const struct i2c_algorithm smbus_algorithm = {
//由于控制器芯片支持smbus,不支持i2c级别。所以这里采用smbus_xfer接口
.smbus_xfer = i801_access,
.functionality = i801_func,
};

i2c控制器 

static const struct i2c_algorithm i2c_imx_algo = {
//i2c 控制器,实现i2c级别数据传输,不支持smbus接口。
.master_xfer = i2c_imx_xfer,
.master_xfer_atomic = i2c_imx_xfer_atomic,
.functionality = i2c_imx_func,
.reg_slave = i2c_imx_reg_slave,
.unreg_slave = i2c_imx_unreg_slave,
};

协议接口使用

对于注册接口的不同,在  drivers\i2c \ i2c-core-smbus.c    __i2c_smbus_xfer 接口可以看出。

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)
{
int (*xfer_func)(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
unsigned long orig_jiffies;
int try;
s32 res;

res = __i2c_check_suspended(adapter);
if (res)
return res;

/* If enabled, the following two tracepoints are conditional on
* read_write and protocol.
*/
trace_smbus_write(adapter, addr, flags, read_write,
command, protocol, data);
trace_smbus_read(adapter, addr, flags, read_write,
command, protocol);

flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;
//查看控制器向协议层注册的smbus_xfer接口
xfer_func = adapter->algo->smbus_xfer;
if (i2c_in_atomic_xfer_mode()) {
if (adapter->algo->smbus_xfer_atomic)
xfer_func = adapter->algo->smbus_xfer_atomic;
else if (adapter->algo->master_xfer_atomic)
xfer_func = NULL; /* fallback to I2C emulation */
}

if (xfer_func) { //对于smbus(i801),此接口不为空,则调用i801驱动接口收发数据
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (res = 0, try = 0; try <= adapter->retries; try++) {
res = xfer_func(adapter, addr, flags, read_write,
command, protocol, data);
if (res != -EAGAIN)
break;
if (time_after(jiffies,
orig_jiffies + adapter->timeout))
break;
}
//如果不是i2c控制器驱动,即master_xfer为空,则直接跳转到trace,
// 不会调用i2c_smbus_xfer_emulated接口进行smbus协议生成。
//

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

res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
command, protocol, data);

trace:
/* If enabled, the reply tracepoint is conditional on read_write. */
trace_smbus_reply(adapter, addr, flags, read_write,
command, protocol, data, res);
trace_smbus_result(adapter, addr, flags, read_write,
command, protocol, res);

return res;
}

总体流程

  

linux i2c smbus驱动_数据

                                           图 1  i2c /smbus 数据传输路径 

数据收发关键数据结构

    以上描述了i2c smbus从应用层到驱动控制器的数据流转路径。

   本章描述在此路径上流转所采用的数据载体形式。在整个i2c 驱动中采用了如下数据结构,此数据结构表示一帧 i2c/smbus数据。

struct i2c_msg {
__u16 addr; //i2c slave device address
__u16 flags;
#define I2C_M_RD 0x0001 /* guaranteed to be 0x0001! */
#define I2C_M_TEN 0x0010 /* use only if I2C_FUNC_10BIT_ADDR */
#define I2C_M_DMA_SAFE 0x0200 /* use only in kernel space */
#define I2C_M_RECV_LEN 0x0400 /* use only if I2C_FUNC_SMBUS_READ_BLOCK_DATA */
#define I2C_M_NO_RD_ACK 0x0800 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART 0x4000 /* use only if I2C_FUNC_NOSTART */
#define I2C_M_STOP 0x8000 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
__u16 len; //传输的数据长度,即i2c一帧数据中,data的个数。
__u8 *buf; //存放数据的缓存。
};

使用示例--eeprom的读写

假设我们采用的eeprom 地址是16位的,eeprom期望的时序(报文协议)如下:

linux i2c smbus驱动_i2c smbus_02

 可以看到此处有两个START,则需要两个i2c_msg数据结构分别携带此两段数据。其中:

  • 第一个start,为写,i2c_msg的字段填写为:   
i2c_msg.addr = slave_addr;  
i2c_msg .flags = I2C_M_WR;
i2c_msg.len = 2; //在第一个START中,数据data即为两个字节的地址,因而这里填写2
i2c_msg.buf[0] = (eep_byte_addr >> 8) & 0x0ff;
i2c_msg.buf[1] = eep_byte_addr & 0x0ff;
  •  第二个start,为读, i2cmsg的字段信息填写为
i2c_msg.addr = slave_addr;  
i2c_msg .flags = I2C_M_RD;
i2c_msg.len = 需要读取的数据长度; //
i2c_msg.buf = 存放读取回来的数据

使用示例--smbus的读写接口

i2cdetect程序里面提供了一套读写接口,最终调用驱动的代码。此处采用两个i2c_msg,即可以支持读,又可以支持写。通常读采用两个i2c_msg完成;写采用一个,可以看到默认msg[1].len=0。

i2c_smbus_xfer_emulated

struct i2c_msg msg[2] = {
{
.addr = addr,
.flags = flags,
.len = 1,
.buf = msgbuf0,
}, {
.addr = addr,
.flags = flags | I2C_M_RD,
.len = 0,
.buf = msgbuf1,
},
};

在smbus的协议中,不论是读,还是写,都存在一个command字段,例如写字节的协议报文

linux i2c smbus驱动_数据_03

此协议只是I2C协议的具体实现,因而其中的command code字段也占用结构体i2c_msg中的buf字段,为buf[0] ,明确这点后就更容易理解i2cdetect的代码实现。

command code既可以表示某个寄存器偏移量,例如要访问slave device 的寄存器 0xa,则在向slave device 发送命令时,此处command code字段填写 0xa.