linuxI2C驅動核心梳理

LIalan發表於2024-05-05

參考文章

  • 《linux裝置驅動開發詳解》第15章
  • 【linux iic子系統】i2c整體框圖【精髓部分】(五)_bus_for_each_drv-CSDN部落格
  • linux裝置驅動程式——i2c匯流排的新增與實現_如何填充i2c_client-CSDN部落格
  • 【linux iic子系統】i2c裝置與驅動匹配過程(三)_i2c_add_driver 如何和clent匹配-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函式。

linuxI2C驅動核心梳理

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資料的函式了!

相關文章