驅動i2c控制器歸根到底是對iic控制器的暫存器進行讀寫,因此,理解了linux中是怎樣通過層層呼叫來操作iic的暫存器,便理解了整個iic子系統的輪廓。
下面以公司使用的重力感測器(bma250)驅動為例來描述這個輪廓。
首先介紹三個比較重要的驅動檔案:
bma250.c(drivers/gsensor/),i2c_core.c(drivers/i2c/),i2c-sunxi.c(/drivers/i2c/busses)。
bma250.c與外接的感測器bma250有關(即client端),
i2c_sunxi.c與主控的iic控制器有關(即adapter端,是對iic暫存器操作的所在檔案),
i2c_core.c則起到為前面兩個檔案進行關聯的作用(實際上就是匯流排管理的作用,而這裡是iic匯流排)。區分開的乙個很大的好處便是移植性強,比如bma250.c可以用於一切支援linux的硬體平台,而
i2c_sunxi.c可用於a10的所有iic外圍器件,硬體變動下,
i2c_core.c則基本不用改變。
在i2c_sunxi.c的模組初始化中定義了乙個平台裝置,用於獲取a10的iic暫存器位址和中斷向量,而在平台裝置匹配成功後呼叫的
i2c_sunxi_probe函式中,初始化了乙個
i2c_adapter結構體,這個結構體用於描述a10的iic控制器,並通過
i2c_add_numbered_adapter函式向iic匯流排新增了新增了這個adapter。
i2c_adapter結構體有個很重要的成員,即
i2c_algorithm結構,該結構下又有乙個很重要的成員:
master_xfer,
i2c_sunxi.c在初始化
i2c_adapter時將
master_xfer初始化為
i2c_sunxi_xfer,而
i2c_sunxi_xfer正是對a10的iic暫存器進行操作的開始。在此函式中,通過直接操作iic暫存器,使能了iic中斷,發出了iic的開始訊號,並通過
wait_event_timeout函式讓程序進入休眠,接下來通過層層中斷(比如iic開始訊號發出完成後會發生中斷,傳送完乙個位元組也會發生中斷)將
i2c結構體中的
msg資料傳送出去,此處
i2c是
i2c_adapter結構體中的
algo_data成員,要傳送的資料都放在這個msg結構體裡。
因此,只需要註冊並呼叫
i2c_adapter->algo->smbus_xfer,便可以讓iic控制器按要求發出或接收資料。
i2c_core.c在完成建設iic匯流排的同時,還完成了對
i2c_adapter->algo->smbus_xfer的封裝,即
i2c_transfer,而
i2c_smbus_read_i2c_block_data之類的函式又對i2c_transfer進行了封裝。因而只需呼叫
i2c_smbus_read_i2c_block_data等函式,並傳入對應的
i2c_adapter,便可以讓控制器按要求發出或接收資料。
在bma250.c的模組初始化中,註冊了乙個
i2c_driver:bma250_driver,該步驟引發了bma250(client)與a10的iic介面(adapter)的匹配。匹配中乙個非常關鍵的因素是bma250_driver中的
address_list,即bma250的iic位址,新版本的核心隱藏了這一步的實現,變得自動化,只需要給出的
address_list正確,硬體電路正確,linux的iic匯流排上掛有對應的adapter,變可以匹配成功,匹配成功後,linux新建了乙個client結構,並將結構裡的adapter初始化為匹配成功的那個adapter,至此,匯流排上,bma250便與a10的iic介面配對,bma250.c中的函式所用到的引數adapter,都是配對成功的那個adapter.
這裡說一下舊版本(2.6和2.6之前)核心client和adapter的匹配過程。在呼叫iic驅動註冊函式時(
i2c_add_driver),引發了
i2c_probe函式的呼叫,在該函式中,把註冊在iic匯流排上的adapter逐個拿出來做實驗,實驗過程如下:將iic有可能的位址0~0x7f,逐個與
address_list裡的位址進行對比,如果相同則呼叫
i2c_smbus_xfer,該函式實際操作了目前這個被實驗中的adatper的暫存器,發出訊號與bma250通訊。若bma250發出了應答訊號,則匹配成功。匹配的實現演算法與硬體無關,並且演算法應該是比較成熟了,所以新核心將其直接拿來用,並將其隱藏起來,不再需要驅動開發者重寫過匹配演算法。
到這裡總結一下:
i2c_core.c建設了一條iic匯流排,i2c-sunxi.c向匯流排新增了乙個adapter,而
bma250.c通過向匯流排註冊乙個iic驅動,探測匯流排上適合(硬體上連線)的adapter,探測成功後bma250便與a10建立聯絡,
bma250.c通過呼叫
i2c_core.c的函式從而呼叫i2c-sunxi.c裡的函式,完成了iic硬體上的實現。
下面以bma250.c為例,簡單說一下上層到下層的呼叫過程。向匯流排註冊完iic驅動後,將匹配到的adapter作為後續函式的引數。bma250驅動通過一工作佇列定時呼叫函式
bma250_read_accel_xyz,該函式讀出了bma250裡的三軸資料,通過輸入子系統,將資料上報給系統。整個呼叫路線如下:
bma250_read_accel_xyz → bma250_smbus_read_byte_block →i2c_smbus_read_i2c_block_data(進入i2c_core.c)→ i2c_smbus_xfer → adapter->algo->smbus_xfer(i2c_sunxi_xfer) → i2c_sunxi_do_xfer(引發中斷)→ i2c_sunxi_handler(iic中斷函式)→i2c_sunxi_core_process→aw_twi_put_byte/aw_twi_get_byte等 →readl/ writel(此函式正是操作a10暫存器的函式)
這便是整個iic子系統的實現輪廓。
以下總結編寫iic驅動所需做的工作:
1、通過平台裝置或其他手段獲取主控iic控制器的資源(暫存器位址和中斷向量號),申請中斷並實現中斷函式。
2、向iic匯流排新增乙個初始化好的
i2c_adapter結構體。而初始化乙個
i2c_adapter結構體時最重要的一步是實現
i2c_adapter中的
algo成員,在algo的成員函式中通過操作暫存器,發出iic起始訊號引發後續資料的傳送與接收(通過中斷,裡面也是通過操作暫存器實現)。
3、向iic匯流排新增乙個初始化好的
i2c_driver結構體。初始化時比較重要的是給出iic裝置的位址。在其
probe函式中完成向應用層介面的初始化,如初始化乙個輸入子系統,用於上報資料。
Linux驅動程式設計 基於I2C子系統的I2C驅動
中,我新增了很多注釋,應該不難理解,有錯誤大家可以指出來,我再改正 include include include include include include include define i2c major 365 主裝置號 define i2c minor 0 從裝置號 define i2c...
linux核心I2C子系統學習(一)
這部分準備分幾個部分進行分析總結 因為i2c的通訊肯定至少要有2個晶元完成,所以它的驅動是由2大部分組成 主晶元的i2c的驅動 從晶元的i2c的驅動 注 萬一選的都不支援咋辦?慘了,只能2個晶元的驅動都得實現了,不過過程差不多 一 主晶元的i2c的驅動 具體如何實現在後面在具體講解 首先要檢視lin...
linux驅動開發擴充套件 i2c子系統核心部分分析
drivers i2c i2c core.c postcore initcall i2c init module exit i2c exit static int init i2c init void endif retval i2c add driver dummy driver 為i2c匯流排新...