Linux設備驅動之I2C設備驅動


   Linux I2C驅動體系結構主要由3部分組成,即I2C設備驅動,I2C核心層、I2C總線驅動。設備驅動層主要是針對不同的I2C硬件從設備編寫的驅動程序,I2C總線驅動是對I2C硬件體系結構中適配器端的實現,適配器可以理解為軟件上抽象出來的i2c接口,這個接口可以對應I2C總線控制器接口,也可以對應用用GPIO模擬的I2C控制器接口。I2C核心層是I2C總線驅動和I2C設備驅動的中間樞紐,它以通用的、與平台無關的接口實現了I2C中設備與適配器的溝通。I2C總線驅動填充i2c_adapter和i2c_algorithm結構體,I2C設備驅動填充i2c_driver和i2c_client結構體。驅動工程師要做的就是總線驅動和設備驅動的編寫。

I2C設備驅動

  前面說了設備驅動主要是填充i2c_driver和i2c_client兩個結構體,了解Linux總線,設備,驅動模型的話就知道,注冊i2c_driver(i2c_client)結構體時,總線會從總線上尋找與其名字匹配的i2c_client(i2c_driver),並調用i2c_driver的probe函數,反之有一個被刪除時,會調用i2c_driver的remove函數。我們可以在i2c_driver的probe函數里做想做的事情,比如創建設備節點等,在remove函數里在一些清除工作。那么i2c_driver該如何定義和注冊呢,參考4.43內核其它的i2c設備驅動文件,我們先定義好i2c_driver結構體,並填充好幾個關鍵成員driver{.name},probe,remove,id_table后,用module_i2c_driver()這個宏來注冊i2c_driver,這個宏在/include/linux/i2c.h定義,注釋上說用它來注冊一個i2c_driver,並通過調用它代替module_init() and module_exit()。當然你也可以在入口函數里用i2c_add_driver,出口函數里用i2c_del_driver來注冊和刪除i2c_driver。這樣,i2c_driver結構體就算完成了。

  接下來我們來注冊一個i2c_client結構體,內核文檔Documentation/i2c/instantiating-devices中介紹了如何實例化一個i2c設備,總共有以下幾種方法:

  Method 1a: Declare the I2C devices by bus number。通過總線號來注冊I2C設備,如下所示:

 1 static struct i2c_board_info h4_i2c_board_info[] __initdata = {
 2     {
 3         I2C_BOARD_INFO("isp1301_omap", 0x2d),
 4         .irq        = OMAP_GPIO_IRQ(125),
 5     },
 6     {    /* EEPROM on mainboard */
 7         I2C_BOARD_INFO("24c01", 0x52),
 8         .platform_data    = &m24c01,
 9     },
10     {    /* EEPROM on cpu card */
11         I2C_BOARD_INFO("24c01", 0x57),
12         .platform_data    = &m24c01,
13     },
14 };
15 
16 static void __init omap_h4_init(void)
17 {
18     (...)
19     i2c_register_board_info(1, h4_i2c_board_info,
20             ARRAY_SIZE(h4_i2c_board_info));
21     (...)
22 }


i2c_register_board_info的傳統用法是在內核初始化時,在i2c_adapter注冊之前。查看i2c_adapter的注冊代碼可以發現,i2c_adapter_register里會調用i2c_scan_static_board_info掃描board_info的鏈表,為每一個注冊的信息調用i2c_new_device函數生成i2c_client,這樣在i2c_driver注冊的時候,設備和驅動就能匹配並調用probe.
如果想在adapter注冊之后調用i2c_register_board_info,注冊的信息沒有機會生成i2c_client,從而無法與i2c_driver匹配。這時還是要使用i2c_new_device或i2c_new_probed_device。

Method 1b: Declare the I2C devices via devicetree,通過設備樹來聲明I2C設備,如下所示:

 1 i2c1: i2c@400a0000 {
 2         /* ... master properties skipped ... */
 3         clock-frequency = <100000>;
 4 
 5         flash@50 {
 6             compatible = "atmel,24c256";
 7             reg = <0x50>;
 8         };
 9 
10         pca9532: gpio@60 {
11             compatible = "nxp,pca9532";
12             gpio-controller;
13             #gpio-cells = <2>;
14             reg = <0x60>;
15         };
16     };

Method 1c: Declare the I2C devices via ACPI。通過ACPI來聲明I2C設備,詳見Documentation/acpi/enumeration.txt。

Method 2: Instantiate the devices explicitly(明確地)。主要有如下兩種方法:

 1 static struct i2c_board_info sfe4001_hwmon_info = {
 2     I2C_BOARD_INFO("max6647", 0x4e),
 3 };
 4 
 5 int sfe4001_init(struct efx_nic *efx)
 6 {
 7     (...)
 8     efx->board_info.hwmon_client =
 9         i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info);
10 
11     (...)
12 }
 1 static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
 2 
 3 static int usb_hcd_nxp_probe(struct platform_device *pdev)
 4 {
 5     (...)
 6     struct i2c_adapter *i2c_adap;
 7     struct i2c_board_info i2c_info;
 8 
 9     (...)
10     i2c_adap = i2c_get_adapter(2);
11     memset(&i2c_info, 0, sizeof(struct i2c_board_info));
12     strlcpy(i2c_info.type, "isp1301_nxp", I2C_NAME_SIZE);
13     isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info,
14                            normal_i2c, NULL);
15     i2c_put_adapter(i2c_adap);
16     (...)
17 }

這類方法適用於事先不知道I2C總線號的情況,前者用i2c_new_device來聲明設備獲得一個i2c_client結構體,后者通過i2c_new_probed_device來實現,兩者的區別在於i2c_new_probed_device會依次探測normal_i2c數組中的地址,查看該地址的設備是否存在,如果存在,就實例化,而i2c_new_device不會管這么多,它會直接實例化某個固定地址的設備,不管它是否存在。兩者都是用i2c_unregister_device()來刪除實例化的設備。

Method 3: Probe(探測) an I2C bus for certain devices。某些時刻,我們不知道I2C設備的一些具體信息,以至於不能用上面的方法,當我們裝載這類設備的驅動時,I2C核心層會為我們探測這些設備並自動實例化。這要求驅動必須有detect函數和address_list成員,address_list為要探測的地址序列。而且總線必須支持該設備,並同意檢測。詳見drivers/hwmon/lm90.c。一般來說這種方法不推薦,更推薦方法一和二。

Method 4: Instantiate from user-space:我們可以從用戶空間實例化一個I2C設備和刪除一個設備。示例如下:

echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device //實例化一個名為eeprom地址為0x50的設備

這種方法只應該在內核中的設備聲明無法使用時才用到,但它也有一些優勢,你不用去重新載入驅動去更改設備的某些設置,你可以在驅動裝載前就實例化它,不用管它需要哪個驅動。

以上就是實例化設備的幾種方法,這樣,我們就把i2c_client和i2c_driver填充完畢了,設備驅動的主要任務也就完成了,具體其它的工作不同的設備各有不同,這里就不介紹了。

   通常來說,I2C設備都由內核驅動來控制,但我們也可以從用戶空間使用一個適配器來使用所有設備,這通過/dev接口來實現,我們需要裝載i2c-dev模塊,我們可以把它理解為一個內核幫我們實現的一個通用I2C設備驅動。如果想要在一個C應用程序里使用這個adapter,需要#include <linux/i2c-dev.h>,這個頭文件在i2c-tool里有,i2c-tool是一款I2C調試工具,將這個頭文件拷入內核的/include/linux/目錄下即可。具體如何在C應用程序里使用這個adapter詳見/Documentation/i2c/dev-interface文檔。


免責聲明!

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



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