博主按:其實老早就想寫這個I2C的了,期間有各種各樣的事情給耽誤了。借着五一放假的時間把這個寫出來,供同志們參考。以后會花一些時間深入研究下內核,雖然以前對內核也有所了解,但是還不系統。I2C的硬件結構並不復雜,一個適配器加幾個設備而已。Linux下驅動的體系結構看着挺復雜,實際也是比較簡單的。在本文中我還是使用實際的例子,結合硬件和軟件兩個方面來介紹。希望能給初學的同志們一些幫助,另外拋磚引玉,希望高手能給一些指點。話不多說,開整!~
本文用到的一些資源:
1. Source Insight軟件
2. mini2440原理圖。 下載地址http://wenku.baidu.com/view/0521ab8da0116c175f0e48fe.html
3. S3C2440 datasheet
4. AT24C08 datasheet
5. Bq27200 datasheet
6. kernel 2.6.31中的At24.c ,Bq27x00_battery.c和i2c-s3c2410.c
7. mini2440的板文件mach-mini2440.c
8. 參考資料:《linux設備驅動開發詳解(第2版)》 by 宋寶華
本文的結構:
第一部分:At24C08驅動
1. mini2440中at24c08的電氣連接
2. Linux中I2C驅動框架分析
3. I2C總線驅動代碼分析
4. at24c08驅動代碼分析
第二部分:Bq27200驅動
1. Bq27200的典型應用電路
2. 主要分析一下ba27x00的代碼,對比at24c08來加深理解。
---------------------我是分割線----------------------
第一部分
1. mini2440中at24c08的電氣連接及其板文件
如下圖。
24C08的I2C接口是與2440的IICSCL/IICSDA直接相連的。在2440內部集成了一個I2C控制器,可以通過寄存器來控制它。先來和這四個寄存器混個臉熟吧,后面分析時還會經常用到這四個寄存器。
在mini2440的板文件中可以找到關於at24c08的內容,如下:
可以看出,在mini2440的init函數中注冊了一個i2c的設備,這個設備我們使用了一個結構體i2c_board_info來描述。這個結構體定義在i2c.h文件中。如下:
其中的platform_data又指向一個at24_platform_data結構體。
以上只是at24c08的部分,在板文件中還可以看到關於2440內部i2c控制器的部分,如下:
其中s2c_device_i2c0定義在arch/arm/plat-s3c/Dev-i2c0.c中(在同一目錄下還可以看到很多Dev-開頭的c文件,都是2440內部集成的各種設備),仔細看下面的代碼再對比2440的datasheet就可以很清楚的知道:
* 控制器的IO起始地址為S3C_PA_IIC =0x54000000,大小是4K,中斷號是43 = IRQ_IIC S3C2410_IRQ(27)
* 控制器名是"s3c2410-i2c"
2. Linux中I2C驅動框架分析
這部分是本文的重點部分。根據上面的電氣連接關系我們可以看出,我們要想操作24c08,必須要做兩方面的驅動。
第一方面: 2440中I2C控制器的驅動,有了這部分驅動,我們才可以操作控制器來產生I2C的時序信號,來發送數據和接收數據。
第二方面: 24C08的驅動,有了這部分驅動,才能使用控制器正確操作芯片,來讀取和存放數據。
在Linux系統中,對上邊第一方面的實現叫做I2C總線驅動,對第二方面的實現叫做I2C設備驅動。一般來說,如果CPU中集成了I2C控制器並且Linux內核支持這個CPU,那么總線驅動方面就不用我們操心了,內核已經做好了。但如果CPU中沒有I2C控制器,而是外接的話,那么就要我們自己實現總線驅動了。對於設備驅動來說,一般常用的驅動也都包含在內核中了,如果我們用了一個內核中沒有的芯片,那么就要自己來寫了。
Linux中I2C體系結構如下圖所示(圖片來源於網絡)。圖中用分割線分成了三個層次:用戶空間(也就是應用程序),內核(也就是驅動部分)和硬件(也就是實際物理設備,這里就是2440中的i2c控制器和at24c08)。這個夠清晰了吧?我們現在就是要研究中間那一層。
由上圖我們還可以看出哪些信息呢?
1). 可以看到幾個重要的組成部分,它們是:Driver,Client,i2c-dev,i2c-core,Algorithm,Adapter。這幾個部分在內核中都有相應的數據結構,定義在i2c.h文件中,盡量避免粘貼打斷代碼來湊數,就不貼出來了。簡要概括一下每個結構體的意義。
Driver --> struct i2c_driver
這個結構體對應了驅動方法,重要成員函數有probe,remove,suspend,resume。
還包括一個重要的數據結構: struct i2c_device_id *id_table; 如果驅動可以支持好幾個設備,那么這里面就要包含這些設備的ID
Client --> struct i2c_client
應用程序是選擇性失明的,它只能看到抽象的設備文件,其他部分都是看不見的。圖中只有Client與應用程序有聯系,所以我們可以大膽得出結論:這個Client是對應於真實的物理設備,在本文就是at24c08。 所以很顯然這個結構體中的內容應該是描述設備的。包含了芯片地址,設備名稱,設備使用的中斷號,設備所依附的控制器,設備所依附的驅動等內容。
Algorithm -->struct i2c_algorithm
Algorithm就是算法的意思。在這個結構體中定義了一套控制器使用的通信方法。其中關鍵函數是master_xfer()。我們實際工作中的重要一點就是要實現這個函數。
Adapter --> struct i2c_adapter
這個結構體對應一個控制器。其中包含了控制器名稱,algorithm數據,控制器設備等。
2). 可以看出,i2c-core起到了關鍵的承上啟下的作用。事實上也是這樣,我們將從這里展開來分析。源代碼位於drivers/i2c/i2c-core.c中。在這個文件中可以看到幾個重要的函數。
*增加/刪除i2c控制器的函數
*增加/刪除設備驅動的函數
*增加/刪除i2c設備的函數
注:在2.6.30版本之前使用的是i2c_attach_client()和i2c_detach_client()函數。之后attach被merge到了i2c_new_device中,而detach直接被unresister取代。實際上這兩個函數內部都是調用了device_register()和device_unregister()。源碼如下:
*I2C傳輸、發送和接收函數
其中send和receive分別都調用了transfer函數,而transfer也不是直接和硬件交互,而是調用algorithm中的master_xfer()函數,所以我們要想進行數據傳輸,必須自己來實現這個master_xfer()函數,這是總線驅動開發的重點之一。下面以read()系統調用的流程來簡單梳理一下:
(待續)