Linux i2c总线(2) - I2C核心层

1. I2C 核心层初始化

        这一部分向系统注册了一个名为i2c的总线类型。

static int __init i2c_init(void)
{
	int retval;
	retval = bus_register(&i2c_bus_type);    //注册i2c总线  /sys/bus/i2c
	retval = i2c_add_driver(&dummy_driver);  //注册一个空设备驱动  /sys/bus/i2c/driver/dummy
	return 0;
}
postcore_initcall(i2c_init);

struct bus_type i2c_bus_type = {
	.name		= "i2c",                //总线的名字
	.match		= i2c_device_match,     //总线下设备与设备驱动的匹配函数
	.probe		= i2c_device_probe,     //总线层的probr函数 
	.remove		= i2c_device_remove,    //总线卸载时执行的函数
	.shutdown	= i2c_device_shutdown,
	.pm		= &i2c_device_pm_ops,   //电源管理
};

int bus_register(struct bus_type *bus)
{
	struct subsys_private *priv;

	priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
	priv->bus = bus;   //互相指向对方
	bus->p = priv;     //以便于由bus可以找到其对应的priv,和由priv可以找到其对应的bus

	klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);  //初始化IIC bus上的devices链表的链表头
	klist_init(&priv->klist_drivers, NULL, NULL);  //初始化IIC bus上的drivers链表的链表头
	return 0;
}

2. 核心层对外提供的注册接口

2.1 注册adapter(i2c控制器)的接口

        i2c_add_adapter / i2c_add_numbered_adapter,这两个接口最终都是调用i2c_register_adapter函数去注册adapter,他们的区别在于:i2c_add_adapter函数是自动分配适配器编号,而i2c_add_numbered_adapter是需要自己手动指定一个适配器编号。

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

	INIT_LIST_HEAD(&adap->userspace_clients);     //初始化i2c_adapter->userspace_clients链表
	if (adap->timeout == 0)
		adap->timeout = HZ;

	dev_set_name(&adap->dev, "i2c-%d", adap->nr);    //设置适配器设备的名字   i2c-%d(nr)
	adap->dev.bus = &i2c_bus_type;       //设置设备的总线类型
	adap->dev.type = &i2c_adapter_type;  //设置设备的设备类型
	res = device_register(&adap->dev);   //注册设备,如果前面没有指定父设备那么创建的设备文件是: /sys/devices/i2c-%d 

	/* bus recovery specific initialization */
	if (adap->bus_recovery_info) {
		struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;

		if (!bri->recover_bus) {
			dev_err(&adap->dev, "No recover_bus() found, not using recovery\n");
			adap->bus_recovery_info = NULL;
			goto exit_recovery;
		}
          ······
	}
exit_recovery:   //如果在注册适配器之前就已经注册了i2c从设备,那么在注册适配器时就会匹配并创建i2c从设备
	//扫描__i2c_board_list链表上挂接的所有的i2c设备信息并与适配器进行匹配,匹配成功创建i2c设备
	if (adap->nr < __i2c_first_dynamic_bus_num)
		i2c_scan_static_board_info(adap);      
	return 0;
}

static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
	struct i2c_devinfo *devinfo;      //定义一个i2c_devinfo 结构体指针

	down_read(&__i2c_board_lock);
	list_for_each_entry(devinfo, &__i2c_board_list, list) {  //  遍历 __i2c_board_list 链表上的所有i2c_devinfo 结构体
 //比较 i2c_devinfo->busnum 与 适配器的编号是否匹配,如果匹配就会调用 i2c_new_device 函数进行注册添加新的设备 i2c_client
		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);
}

2.2 注册i2c_client(slave设备)的接口

        i2c_new_device()就是注册i2c从设备的接口,这个接口是对外开放的。这个接口有两种使用方式,一种是注册adaptor时被调用,另一种是在其它模块中直接调用该接口注册i2c从设备。

struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
	struct i2c_client *client;         //定义一个 i2c_client 指针
	int status;

	client = kzalloc(sizeof *client, GFP_KERNEL);
    //对i2c_client结构体变量进行填充
	client->adapter = adap;        //i2c从机设备通过i2c_client->adapter指针去指向与它匹配成功的适配器i2c_adapter
	client->dev.platform_data = info->platform_data;    //将传进来的i2c_board_info结构体作为i2c从机设备的platform平台数据

	if (info->archdata)
		client->dev.archdata = *info->archdata;

	client->flags = info->flags;    //标志位
	client->addr = info->addr;      //i2c从机设备的地址
	client->irq = info->irq;        //中断号
	strlcpy(client->name, info->type, sizeof(client->name));    //名字

	/* Check for address validity */
	status = i2c_check_client_addr_validity(client);     //从机设备地址校验
	
	/* Check for address business */
	status = i2c_check_addr_busy(adap, client->addr);

	client->dev.parent = &client->adapter->dev;    //指定i2c 从机设备的父设备是与它匹配成功的适配器对应的设备
	client->dev.bus = &i2c_bus_type;               //指定从机设备的总线类型
	client->dev.type = &i2c_client_type;           //指定从机设备的设备类型
	client->dev.of_node = info->of_node;
	ACPI_HANDLE_SET(&client->dev, info->acpi_node.handle);

	/* For 10-bit clients, add an arbitrary offset to avoid collisions */
	dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),      //设置次设备的名字%d-%04x
		     client->addr | ((client->flags & I2C_CLIENT_TEN) ? 0xa000 : 0));
	status = device_register(&client->dev);      //注册从设备  --->

	return client;
}

2.3 注册device_driver(slave设备驱动)的接口

        i2c_add_driver() 或者 i2c_register_driver()是注册i2c 从设备驱动的接口,其实这两个接口是同一个接口,前者是一个宏,我觉得完全没必要如此,因为i2c_register_driver() 接口并没有被声明为静态,另外定义个宏有点多此一举的意思!!!

//kernel3.10/include/linux/i2c.h
#define i2c_add_driver(driver) 	i2c_register_driver(THIS_MODULE, driver)

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
	int res;

	/* Can't register until after driver model init */
	if (unlikely(WARN_ON(!i2c_bus_type.p)))
		return -EAGAIN;

	/* add the driver to the list of i2c drivers in the driver core */
	driver->driver.owner = owner;
	driver->driver.bus = &i2c_bus_type;     //指定该设备驱动的总线类型  i2c

	/* When registration returns, the driver core
	 * will have called probe() for all matching-but-unbound devices.
	 */
	res = driver_register(&driver->driver);   //注册设备驱动 --->
	if (res)
		return res;

	/* Drivers should switch to dev_pm_ops instead. */
	if (driver->suspend)
		pr_warn("i2c-core: driver [%s] using legacy suspend method\n",
			driver->driver.name);
	if (driver->resume)
		pr_warn("i2c-core: driver [%s] using legacy resume method\n",
			driver->driver.name);

	pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);

	INIT_LIST_HEAD(&driver->clients);   // 初始化i2c_driver -> clients 链表
	/* Walk the adapters that are already present */
	i2c_for_each_dev(driver, __process_new_driver);

	return 0;
}

2.4 数据传输接口(i2c控制器和i2c从设备之间)

        i2c core层给slave设备驱动提供了读写接口,从设备驱动程序直接调用该类接口就可以使IIC控制器和从设备通信,以写操作(i2c_smbus_write_byte)为例:

s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value)
{
        return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
                              I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL);
}
EXPORT_SYMBOL(i2c_smbus_write_byte);

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)
{
        if (adapter->algo->smbus_xfer) {
                i2c_lock_adapter(adapter);
                /* Retry automatically on arbitration loss */
                orig_jiffies = jiffies;
                for (res = 0, try = 0; try <= adapter->retries; try++) {
                    /* 调用控制器驱动(适配器adaptor)中注册的smbus_xfer回调函数进行数据传输,这里可以和 */
                        res = adapter->algo->smbus_xfer(adapter, addr, flags,
                                                        read_write, command,
                                                        protocol, data);
                        if (res != -EAGAIN)
                                break;
                        if (time_after(jiffies,
                                       orig_jiffies + adapter->timeout))
                                break;
                }
        }
}
EXPORT_SYMBOL(i2c_smbus_xfer);

        从I2C总线(一)的相关结构体介绍中可知,struct i2c_adapter是用来描述一个I2C适配器,该结构体中包含一个struct i2c_algorithm结构体,代表的是适配器的通信算法,适配器在注册的时候会填充i2c_algorithm结构体中的回调函数,比如smbus_xfer。

3. 补充

        注册i2c_client 和device_driver 的接口最终各自调用了device_register()和driver_register()两个接口,这两个接口的实现分别在driver/base/core.c和driver/base/driver.c中,都是属于驱动模型中的基础接口,这两个接口最终都会调用driver里的match函数进行匹配,调用probe函数进行绑定和初始化,这两个接口我在分析platform bus时仔细分析过,详细过程请回看《Platform Bus(二)》《Platform Bus(三)》

4. 小结

       I2C驱动有4个重要的东西,I2C总线、I2C驱动、I2C设备、I2C设备器:

I2C总线:维护着两个链表(I2C驱动、I2C设备),管理I2C设备和I2C驱动的匹配和删除等
I2C驱动:对应的就是I2C设备的驱动程序
I2C设备:是具体硬件设备的一个抽象
I2C设配器:用于I2C驱动和I2C设备间的通用,是SOC上I2C控制器的一个抽象

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值