linux中i2c驱动框架分析

本文详细解释了Linux内核中I2C子系统的关键结构体(如i2c_adapter、i2c_client、i2c_driver和i2c_algorithm)之间的关系,包括物理适配器的配置、客户端设备的注册、驱动程序的匹配以及通信方法。特别强调了adapter的初始化过程和实际通信机制,以及client与driver的交互作用。
摘要由CSDN通过智能技术生成

参考文章

总的图如下所示
在这里插入图片描述

下面根据图具体解释。

重要的几个结构体的关系

比较重要的是 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数据的函数了!

  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值