I2C总线
Posted 四季帆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了I2C总线相关的知识,希望对你有一定的参考价值。
1. I2C bus初始化
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_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的接口
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的接口
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;
}
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(三)》。
以上是关于I2C总线的主要内容,如果未能解决你的问题,请参考以下文章