linux驱动之i2c总线驱动调用分析【基于linux4.4】

平台:RK3399

使用设备树描述板级资源;

框架:

linux i2c框架同样采用分层、分离的模式设计;从上到下分为  app调用层、i2c core层、驱动层;驱动层又分为 cpu平台 i2c控制器相关的驱动层以及 i2c总线上挂接的设备驱动程序;而分离的思想则体现在板级相关的信息放在设备树上实现,而通用的读写、初始化流程、操作流程等则放到驱动里面实现(类似platform总线驱动);

具体分析:

1. cpu i2c控制器驱动(adpter)

 

static struct platform_driver rk3x_i2c_driver = {
    .probe   = rk3x_i2c_probe,
    .remove  = rk3x_i2c_remove,
    .driver  = {
        .name  = "rk3x-i2c",
        .of_match_table = rk3x_i2c_match,
        .pm = &rk3x_i2c_pm_ops,
    },
};
module_platform_driver(rk3x_i2c_driver)

RK3399采用以上方法向内核定义了一个platform driver,内核启动的时候,会再设备树里面I2C节点找到对应的设备节点定义compatible = "rockchip,rk3399-i2c"; ,由于 of_match_table 里面可以找到rockchip,rk3399-i2c,所以紧接着会调用 rk3x_i2c_probe 函数;rk3x_i2c_probe 函数主要是向内核注册了cpu i2c控制器驱动,并且扫描设备树里面i2c所有节点的设备信息(包括I2C根节点的信息),然后将添加扫描到的i2c设备信息加入i2c bus总线维护的链表里面 (bus->p->klist_devices),然后再用设备的名称和 i2c设备驱动里面的设备名称匹配,如果匹配成功,则调用i2c设备的probe函数

具体分析如下:

ret = i2c_add_adapter(&i2c->adap);

adpter注册及device注册调用流程如下:

ret = i2c_add_adapter(&i2c->adap);
    i2c_register_adapter(adapter);
        of_i2c_register_devices(adap); //扫描设备树I2C总线子节点信息;包括挂接的设备名称及地址!
            for_each_available_child_of_node(adap->dev.of_node, node) {
            if (of_node_test_and_set_flag(node, OF_POPULATED))
                continue;
            of_i2c_register_device(adap, node);
          }

再深入 of_i2c_register_device(adap, node) 函数调用分析:

of_i2c_register_device(adap, node)
    i2c_new_device(adap, &info); // info里面便包含了i2c总线挂接设备的名称及地址
        status = device_register(&client->dev); // 完成 client 设备的创建
            return device_add(dev);
                error = bus_add_device(dev); // 将设备加入bus总线
                bus_probe_device(dev); // 开始设备的匹配

再深入看下 bus_probe_device(dev); // 开始设备的匹配

void bus_probe_device(struct device *dev)
{
    struct bus_type *bus = dev->bus;
    struct subsys_interface *sif;

    if (!bus)
        return;

    if (bus->p->drivers_autoprobe) // 在i2c_core初始化的时候已经置一
        device_initial_probe(dev);  

    mutex_lock(&bus->p->mutex);
    list_for_each_entry(sif, &bus->p->interfaces, node)
        if (sif->add_dev)
            sif->add_dev(dev, sif);
    mutex_unlock(&bus->p->mutex);
}

关注以上代码里面的  device_initial_probe(dev); 函数,其调用流程如下

device_initial_probe(dev);
    __device_attach(dev, true);
        ret = bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver);
      if (!driver_match_device(drv, dev)) // 开始调用 i2c bus总线的match函数,匹配设备和设备驱动
      return driver_probe_device(drv, dev); // 匹配成功,则调用设备驱动的probe函数

至此, adpter及设备端的注册匹配基本结束;

另外还有设备驱动部分,拿声卡 es8316来分析;

先分配一个 i2c driver结构体

static struct i2c_driver es8316_i2c_driver = {
    .driver = {
        .name        = "es8316",
        .of_match_table = es8316_of_match,
    },
    .probe    = es8316_i2c_probe,
    .remove   = es8316_i2c_remove,
    .shutdown = es8316_i2c_shutdown,
    .id_table = es8316_i2c_id,
};

module_i2c_driver(es8316_i2c_driver);
module_i2c_driver(es8316_i2c_driver)向内核注册i2c driver;主要完成了设备驱动的注册,及跟设备的匹配,如果匹配成功,则调用probe函数 es8316_i2c_probe, 具体流程如下
i2c_register_driver
    res = driver_register(&driver->driver);
        ret = bus_add_driver(drv);
            klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
            error = driver_attach(drv);

从上面流程可以看出,已经完成了把驱动放入 bus 总线维护的 drivers 链表  klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); 

再具体深入下 driver_attach(drv);

driver_attach(drv);
    return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
        if (!driver_match_device(drv, dev))
        driver_probe_device(drv, dev);

至此,便完成了设备驱动与设备的匹配和设备驱动probe的调用

至此,i2c驱动基本完成,而其余跟设备相关操作,均可在probe函数里实现,比如声卡的初始化等;

 

  

猜你喜欢

转载自www.cnblogs.com/weishengzhong/p/12183182.html
今日推荐