linux i2c smbus驱动

目录

目录

数据收发流程

驱动层 收发接口

驱动层注册

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_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字段,例如写字节的协议报文

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

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

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

proware

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值