linuxI2C驱动核心梳理

参考文章

驱动和设备树以及各个结构体总的关系图如下所示:

下面根据图具体解释。

重要的几个结构体的关系

比较重要的是 i2c_adapter i2c_client i2c_driver i2c_algorithm

i2c_adapter 对应的一个物理上的适配器,以tegra为例,在设备树中compitable = "nvidia,tegra210-i2c" 来指示适配器该怎么配置。并且在其对应的probe函数中会同时注册 i2c_algorithm 的方法用来控制如何发送i2c数据,以及发送i2c数据的函数

i2c_client 会通过对应设备树中的节点信息填充,然后挂到bus上统一被管理。client其实就是设备树信息。所以一个adapter可能会有多个client(=一个i2c总线上挂载多个设备)

i2c_driver 是用户所写的驱动程序,也会被统一挂载到bus上。由bus统一管理。

i2c bus部分

bus提供了注册和probe方法。并由于很多设备都依赖i2c,所以i2c bus的注册优先级很高

//i2c-core.c postcore_initcall(i2c_init); // 2

i2c_init 中,会用bus_register注册一个 i2c_bus_type

struct bus_type i2c_bus_type = { .name = "i2c", .match = i2c_device_match, .probe = i2c_device_probe, .remove = i2c_device_remove, .shutdown = i2c_device_shutdown, };

在这个总线类型中,会提供match方法和probe方法。match就是用来匹配总线上的设备树信息client,和驱动设备文件driver的。probe用来在driver的probe(就是"xx.ko"驱动文件里的probe!!)执行前做一些初始化,然后再去调用driver提供的probe函数。

adapter部分

adapter是控制实际的物理i2c适配器与外界设备通信的。

物理上的i2c1的设备树描述

i2c@7000c000 { compatible = "nvidia,tegra210-i2c", "nvidia,tegra114-i2c"; reg = <0x0 0x7000c000 0x0 0x100>; interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; clocks = <&tegra_car TEGRA210_CLK_I2C1>; clock-names = "div-clk"; resets = <&tegra_car 12>; reset-names = "i2c"; dmas = <&apbdma 21>, <&apbdma 21>; dma-names = "rx", "tx"; status = "disabled"; };

在tegra的厂商提供的,会将 nvidia,tegra210-i2c 节点认为是adapter。

//i2c-tegra.c subsys_initcall(tegra_i2c_init_driver);//优先级是小于bus的 static const struct of_device_id tegra_i2c_of_match[] = { ... { .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, }, ... {}, };

在对应的probe函数中

  • 注册对应的发送函数
  • 通过 i2c_add_numbered_adapter 注册到系统中

probe中还会 i2c_add_driver(&dummy_driver) 注册一个空的驱动

i2c-dev.c 中提供了adapter的文件功能和一些文件操作的接口
i2c-dev.c的initcall优先级很低只有6
所以如果有已经注册的adapter会依次注册

//i2c-dev.c /* Bind to already existing adapters right away */ i2c_for_each_dev(NULL, i2cdev_attach_adapter);

通信方法

通过在probe中注册的 algorithm 控制。

如果adapter没有algorithm提供的发送函数,那么就无法通信。最后实际通信会落脚到algorithm

//i2c-tegra.c tegra_i2c_probe{ ... i2c_dev->adapter.algo = &tegra_i2c_algo; ... } static const struct i2c_algorithm tegra_i2c_algo = { .master_xfer = tegra_i2c_xfer, //实际的发送函数 .functionality = tegra_i2c_func, //控制访问周期 };

tegra的实际的发送函数tegra_i2c_xfer是比较复杂的,会涉及到dma的方式,并且最后是通过发送完后的中断来最后发送停止信号。这部分将在后续的文章中详细分析。

client部分

用来描述挂载在i2c1下的i2c设备

i2c@7000c400{ motor@60{ compatible = "nxp,pca9685-pwm"; #pwm-cells = <2>; reg = <0x60>; invert; }; };

在注册的时候会通过 i2c_new_device将设备树的内容填充到 i2c_client 中,然后设置好 i2c_client_type 用来指示是i2c设备。最后通过 device_register 将设备注册到系统中。

device_register会调用 device_add ,这个函数会创建好文件,以及匹配对应的总线,并且通知链表来监听总线设备,有新的设备加入。同时会产生一个uevent,有设备加入,最后会去匹配总线上驱动。

这一部分也是比较复杂,后续可能会专门有分析的文章。

可以参考下面的文章【linux iic子系统】i2c设备与驱动匹配过程(三)_i2c_add_driver 如何和clent匹配-CSDN博客

driver部分

driver就是用户写的驱动程序,通过id_table或者另外的方式通过bus匹配到client后,会先调用bus的probe函数然后,将client传入driver的probe函数中供driver使用,(这也就是driver的probe函数都会有client结构的原因!)

并且client是依赖于adapter的所以,通过client就可以调用到发送i2c数据的函数了!


__EOF__

本文作者alan
本文链接https://www.cnblogs.com/alanli07/p/18173969.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   LIalan  阅读(224)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· 【全网最全教程】使用最强DeepSeekR1+联网的火山引擎,没有生成长度限制,DeepSeek本体
点击右上角即可分享
微信分享提示