目录
目录
数据收发流程
驱动层 收发接口
驱动层注册
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;
}
总体流程
图 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期望的时序(报文协议)如下:
可以看到此处有两个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_emulatedstruct 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字段,例如写字节的协议报文
此协议只是I2C协议的具体实现,因而其中的command code字段也占用结构体i2c_msg中的buf字段,为buf[0] ,明确这点后就更容易理解i2cdetect的代码实现。
command code既可以表示某个寄存器偏移量,例如要访问slave device 的寄存器 0xa,则在向slave device 发送命令时,此处command code字段填写 0xa.