i2c 子系统分析

一、i2c驱动核心层分析

i2c驱动核心层主要为设备驱动层提供设备注册、通信、管理等接口,同时连接i2c控制器驱动。这里首先分析i2c子系统中比较重要的几个结构体:

1、driver

struct i2c_driver {
	unsigned int class;   	// 驱动支持的设备类型,如传感器、eeprom等

        /* Notifies the driver that a new bus has appeared or is about to be
	 * removed. You should avoid using this if you can, it will probably
	 * be removed in a near future.
	 */
	int (*attach_adapter)(struct i2c_adapter *);    // 旧版探测函数
	int (*detach_adapter)(struct i2c_adapter *);
	/* Standard driver model interfaces */
	int (*probe)(struct i2c_client *, const struct i2c_device_id *);
	int (*remove)(struct i2c_client *);

	/* driver model interfaces that don't relate to enumeration  */
	void (*shutdown)(struct i2c_client *);
	int (*suspend)(struct i2c_client *, pm_message_t mesg);
	int (*resume)(struct i2c_client *);

	/* Alert callback, for example for the SMBus alert protocol.
	 * The format and meaning of the data value depends on the protocol.
	 * For the SMBus alert protocol, there is a single bit of data passed
	 * as the alert response's low bit ("event flag").
	 */
	void (*alert)(struct i2c_client *, unsigned int data);

	/* a ioctl like command that can be used to perform specific functions
	 * with the device.
	 */
	int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

	struct device_driver driver;             // 驱动模型
	const struct i2c_device_id *id_table;    // 驱动支持设备的信息

	/* Device detection callback for automatic device creation */
	int (*detect)(struct i2c_client *, struct i2c_board_info *);    // 探测函数
	const unsigned short *address_list;      // 驱动支持的设备地址,传递给detect函数
	struct list_head clients;                // 驱动节点
};

这个结构体代表了一个i2c设备驱动,其中class代表i2c器件的类型,attach_adapter和detach_adapter代表从总线上添加或者删除设备,probe和remove用于绑定和解除绑定,alert用于SMBus,driver是设备的驱动模型,id_tabel代表驱动所支持设备的信息,detect用于设备探测,address_list传递给detect,clients代表我们创建的客户端。 

2、device

struct i2c_client {
	unsigned short flags;		    // 设备标志
	unsigned short addr;		    // 设备地址-7位
	char name[I2C_NAME_SIZE];       // 设备名称
	struct i2c_adapter *adapter;	// 设备所属的适配器
	struct i2c_driver *driver;	    // 设备所用的驱动
	struct device dev;		        // 设备模型
	int irq;			            // 设备可能会用到的中断号
	struct list_head detected;
};

该结构体代表一个i2c从设备,flags指定需要的特殊处理方式,addr是i2c设备的7位地址值,name是i2c设备名称,adapter为设备所属的适配器,driver为此设备的驱动程序,dev为设备的设备模型,irq为设备可能会用到的中断号。

struct i2c_board_info {
	char		type[I2C_NAME_SIZE];        // 设备名称
	unsigned short	flags;                  // 设备标志,如读取标志、十位地址位等
	unsigned short	addr;                   // 设备地址
	void		*platform_data;             // 设备私有数据
	struct dev_archdata	*archdata;
#ifdef CONFIG_OF
	struct device_node *of_node;
#endif
	int		irq;                            // 设备采用的中断号
};

这个结构体通常用于创建i2c设备,type为设备名称,platform_data为platform总线信息。通常采用如下方式注册i2c设备: 

static struct i2c_board_info __initdata i2c_devices0[]= {
       #ifdefined(CONFIG_REGULATOR_AXP192)
       {
              I2C_BOARD_INFO("axp192",0x34),
              .irq           = PMIC_IRQ,
              .platform_data= &axp192_info,
       },
       #endif
};

该数组包含一个通道上所有设备的信息,然后通过i2c_register_board_info函数注册该通道上所有设备。

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;
}

其中busnum指定通道,info指定设备信息,len指定设备数量,该函数将设备依次添加到全局设备链表__i2c_board_list中。链表节点devinfo中有设备信息以及设备所连接的通道。

struct i2c_devinfo {
	struct list_head	list;              // 设备节点
	int			busnum;                    // i2c通道
	struct i2c_board_info	board_info;    // 设备信息
};

函数的调用方式如下:

i2c_register_board_info(0, i2c_devices0, ARRAY_SIZE(i2c_devices0));

3、adapter

struct i2c_adapter {
	struct module *owner;
	unsigned int id;
	unsigned int class;		               // 适配器支持的类型
	const struct i2c_algorithm *algo;      // 适配器通信的方法
	void *algo_data;                       // 适配器私有数据

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

	int timeout;			               // 超时时间
	int retries;                           // i2c超时重试次数
	struct device dev;		               // 设备模型
	int nr;                                // 适配器为i2c的第几通道
	char name[48];                         // 适配器名称
	struct completion dev_released;

	struct list_head userspace_clients;
};

i2c_adapter代表一个适配器,一个i2c通道就是一个适配器,algo指定该适配器的方法。 

4、algorithm

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 (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
			   unsigned short flags, char read_write,
			   u8 command, int size, union i2c_smbus_data *data);

	/* 获取适配器支持的通信方式 */
	u32 (*functionality) (struct i2c_adapter *);
};

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
	unsigned long orig_jiffies;
	int ret, try;

	/* 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 = rt_mutex_trylock(&adap->bus_lock);
			if (!ret)
				/* I2C activity is ongoing. */
				return -EAGAIN;
		} else {
			rt_mutex_lock(&adap->bus_lock
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值