Linux i2c驱动框架分析 (三)

Linux i2c驱动框架分析 (一)
Linux i2c驱动框架分析 (二)
Linux i2c驱动框架分析 (三)
通用i2c设备驱动分析

i2c适配器驱动

i2c适配器驱动加载与卸载

i2c总线驱动模块的加载函数要完成两个工作。

  1. 初始化i2c适配器所使用的硬件资源,如申请I/O地址、中断号等。

  2. 初始化好i2c_adapter数据结构的成员,然后通过i2c_add_adapter()添加i2c_adapter的数据结构。

i2c总线驱动模块的卸载函数要完成的工作与加载函数相反。

  1. 释放i2c适配器所使用的硬件资源,如释放 I/O地址、中断号等。

  2. 通过i2c_del_adapter()删除i2c_adapter的数据结构。

i2c总线通信方法

我们需要为特定的i2c适配器实现其通信方法,主要实现i2c_algorithm的master_xfer()函数和functionality()函数。

functionality()函数非常简单,用于返回algorithm所支持的通信协议,如I2C_FUNC_I2C、I2C_ FUNC_10BIT_ADDR、I2C_FUNC_SMBUS_READ_BYTE、I2C_FUNC_SMBUS_WRITE_BYTE等。

master_xfer()函数在i2c适配器上完成传递给它的i2c_msg数组中的每个i2c消息。

注册适配器驱动

调用i2c_add_adapter()函数注册一个适配器:

int i2c_add_adapter(struct i2c_adapter *adapter)
{
	struct device *dev = &adapter->dev;
	int id;

	//对于在设备树定义的i2c适配器,则通过设备树获得总线号
	if (dev->of_node) {
		id = of_alias_get_id(dev->of_node, "i2c");
		if (id >= 0) {
			adapter->nr = id; 
			//已明确总线号的适配器,调用__i2c_add_numbered_adapter函数进行注册
			return __i2c_add_numbered_adapter(adapter);
		}
	}

	mutex_lock(&core_lock);

	//从i2c_adapter_idr中申请一个可用的总线号
	id = idr_alloc(&i2c_adapter_idr, adapter,
		       __i2c_first_dynamic_bus_num, 0, GFP_KERNEL);
	mutex_unlock(&core_lock);
	if (WARN(id < 0, "couldn't get idr"))
		return id;

	adapter->nr = id;

	return i2c_register_adapter(adapter);
}

__i2c_add_numbered_adapter函数是对i2c_register_adapter函数的封装,真正注册函数是后者,定义如下:

static int i2c_register_adapter(struct i2c_adapter *adap)
{
	int res = -EINVAL;

	......

	/* 检查是否设置name */
	if (WARN(!adap->name[0], "i2c adapter has no name"))
		goto out_list;

	//检查是否提供algo
	if (!adap->algo) {
		pr_err("adapter '%s': no algo supplied!\n", adap->name);
		goto out_list;
	}

	if (!adap->lock_ops)
		adap->lock_ops = &i2c_adapter_lock_ops;

	//初始化一些锁及链表
	rt_mutex_init(&adap->bus_lock);
	rt_mutex_init(&adap->mux_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;

	//设置adapter device
	dev_set_name(&adap->dev, "i2c-%d", adap->nr);
	adap->dev.bus = &i2c_bus_type;
	adap->dev.type = &i2c_adapter_type;

	//注册adapter device
	res = device_register(&adap->dev);
	if (res) {
		pr_err("adapter '%s': can't register device (%d)\n", adap->name, res);
		goto out_list;
	}

	......


	/* 前面章节提到过,可以在i2c适配器节点下描述的i2c设备节点
     * of_i2c_register_devices函数就是处理这些i2c设备节点,并注册设备
     */
	of_i2c_register_devices(adap);
	......

	/* 以前的内核不支持设备树,通过i2c_register_board_info函数注册i2c设备信息
   * 这些设备信息会插入到一个__i2c_board_list链表里,注册适配器时,会从这个
   * 链表里取出设备信息,并注册i2c设备
   */
	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设备节点

在这里插入图片描述
在注册i2c适配器时,会调用of_i2c_register_devices函数来处理这些子节点:

static void of_i2c_register_devices(struct i2c_adapter *adap)
{
	struct device_node *bus, *node;
	struct i2c_client *client;

	/* 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");

	bus = of_get_child_by_name(adap->dev.of_node, "i2c-bus");
	if (!bus)
		bus = of_node_get(adap->dev.of_node);

	//遍历子节点,调用of_i2c_register_device函数注册i2c设备
	for_each_available_child_of_node(bus, node) {
		if (of_node_test_and_set_flag(node, OF_POPULATED))
			continue;

		//注册i2c设备
		client = of_i2c_register_device(adap, node);
		if (IS_ERR(client)) {
			dev_warn(&adap->dev,
				 "Failed to create I2C device for %s\n",
				 node->full_name);
			of_node_clear_flag(node, OF_POPULATED);
		}
	}

	of_node_put(bus);
}

of_i2c_register_device:

static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
						 struct device_node *node)
{
	struct i2c_client *result;
	struct i2c_board_info info = {};
	struct dev_archdata dev_ad = {};
	const __be32 *addr_be;
	u32 addr;
	int len;

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

	//从节点中获取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);
		return ERR_PTR(-EINVAL);
	}

	//节点的reg属性,表示设备地址
	addr_be = of_get_property(node, "reg", &len);
	if (!addr_be || (len < sizeof(*addr_be))) {
		dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
			node->full_name);
		return ERR_PTR(-EINVAL);
	}

	......

	//判断地址使用有效
	if (i2c_check_addr_validity(addr, info.flags)) {
		dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
			addr, node->full_name);
		return ERR_PTR(-EINVAL);
	}

	//从节点中得到相关信息,用于初始化一个i2c_board_info 
	info.addr = addr;
	info.of_node = of_node_get(node);
	info.archdata = &dev_ad;

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

	//增添i2c设备
	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);
		return ERR_PTR(-EINVAL);
	}
	return result;
}

这里关注一下,从节点中是怎么获取name的,of_modalias_node:

int of_modalias_node(struct device_node *node, char *modalias, int len)
{
	const char *compatible, *p;
	int cplen;

	/* 从compatible属性获取name,从代码上可知,如果compatible为"wlf,wm8960"
   * 那么设备的name会取','之后的字符串,即wm8960,只是要注意的点
   */
	compatible = of_get_property(node, "compatible", &cplen);
	if (!compatible || strlen(compatible) > cplen)
		return -ENODEV;
	p = strchr(compatible, ',');
	strlcpy(modalias, p ? p + 1 : compatible, len);
	return 0;
}

注册静态定义的i2c设备

以前的内核不支持设备树,通过i2c_register_board_info函数注册i2c设备信息,这些设备信息会插入到一个__i2c_board_list链表里,注册适配器时,会从这个链表里取出设备信息,并注册i2c设备。

static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
	struct i2c_devinfo	*devinfo;

	down_read(&__i2c_board_lock);
	//判断总线num是否一致,一致则i2c_new_device
	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);
}

通知驱动

注册适配器时,会通知驱动程序去探测设备:

static int i2c_register_adapter(struct i2c_adapter *adap)
{

	......
	//遍历i2c bus上的驱动链表,调用__process_new_adapter函数
	bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
	
	......

}

__process_new_adapter函数前面已分析,即去探测驱动的address_list定义的设备地址是否存在,存在则注册设备。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值