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函數里實現,比如聲卡的初始化等;

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM