Linux I2C 從裝置介面描述

作者:Wolfram Sang <wsa@sang-engineering.com>,2014-15 年

如果使用的 I2C 控制器具有從裝置功能,Linux 也可以充當 I2C 從裝置。為此,需要在匯流排驅動程式中提供從裝置支援,以及提供實際功能的硬體獨立的軟體後端。後者的一個例子是從裝置 eeprom 驅動程式,它充當雙記憶體驅動程式。當總線上的另一個 I2C 主裝置可以像訪問常規 EEPROM 一樣訪問它時,Linux I2C 從裝置可以透過 sysfs 訪問內容並根據需要處理資料。後端驅動程式和 I2C 匯流排驅動程式透過事件進行通訊。這是一個小圖,可視化了資料流和資料傳輸的方式。虛線僅標記一個示例。後端也可以使用字元裝置,僅在核心中,或者完全不同的東西

            e.g. sysfs        I2C slave events        I/O registers
+-----------+   v    +---------+     v     +--------+  v  +------------+
| Userspace +........+ Backend +-----------+ Driver +-----+ Controller |
+-----------+        +---------+           +--------+     +------------+
                                                              | |
----------------------------------------------------------------+--  I2C
--------------------------------------------------------------+----  Bus

注意:從技術上講,後端和驅動程式之間還存在 I2C 核心。但是,在撰寫本文時,該層是透明的。

使用者手冊

I2C 從裝置後端的行為類似於標準 I2C 客戶端。因此,您可以按照文件 如何例項化 I2C 裝置 中的描述來例項化它們。唯一的區別是 I2C 從裝置後端有自己的地址空間。因此,您必須將 0x1000 新增到您最初請求的地址。以下是一個從使用者空間在匯流排 1 上以 7 位地址 0x64 例項化 slave-eeprom 驅動程式的示例

# echo slave-24c02 0x1064 > /sys/bus/i2c/devices/i2c-1/new_device

每個後端都應附帶單獨的文件,以描述其特定行為和設定。

開發者手冊

首先,將詳細描述匯流排驅動程式和後端使用的事件。之後,將提供一些用於擴充套件匯流排驅動程式和編寫後端的實現提示。

I2C 從裝置事件

匯流排驅動程式使用以下函式將事件傳送到後端

ret = i2c_slave_event(client, event, &val)

“client” 描述 I2C 從裝置。“event” 是以下描述的特殊事件型別之一。“val” 儲存要讀取/寫入的資料位元組的 u8 值,因此是雙向的。即使 val 未用於事件,也必須始終提供指向 val 的指標,即不要在此處使用 NULL。“ret” 是來自後端的返回值。匯流排驅動程式必須提供強制性事件,後端驅動程式必須檢查這些事件。

事件型別

  • I2C_SLAVE_WRITE_REQUESTED (強制)

    “val”: 未使用

    “ret”: 如果後端已準備就緒,則為 0,否則為某個 errno

另一個 I2C 主裝置想要向我們寫入資料。應該在我們自己的地址和寫入位被檢測到後傳送此事件。資料尚未到達,因此沒有任何要處理或返回的內容。返回後,匯流排驅動程式必須始終確認地址階段。如果“ret”為零,則完成後端初始化或喚醒,並且可以接收更多資料。如果“ret”是 errno,則匯流排驅動程式應否認所有傳入位元組,直到下一個停止條件以強制重試傳輸。

  • I2C_SLAVE_READ_REQUESTED (強制)

    “val”: 後端返回要傳送的第一個位元組

    “ret”: 始終為 0

另一個 I2C 主裝置想要從我們這裡讀取資料。應該在我們自己的地址和讀取位被檢測到後傳送此事件。返回後,匯流排驅動程式應傳輸第一個位元組。

  • I2C_SLAVE_WRITE_RECEIVED (強制)

    “val”: 匯流排驅動程式傳遞接收到的位元組

    “ret”: 如果應該確認該位元組,則為 0,如果應該否認該位元組,則為某個 errno

另一個 I2C 主裝置已向我們傳送一個位元組,該位元組需要設定在“val”中。如果“ret”為零,則匯流排驅動程式應確認此位元組。如果“ret”是 errno,則應否認該位元組。

  • I2C_SLAVE_READ_PROCESSED (強制)

    “val”: 後端返回要傳送的下一個位元組

    “ret”: 始終為 0

匯流排驅動程式請求下一個要傳送給另一個 I2C 主裝置的位元組,該位元組位於“val”中。重要提示:這並不意味著先前的位元組已被確認,它僅表示先前的位元組已移出到匯流排!為了確保無縫傳輸,大多數硬體在先前的位元組仍在移出時請求下一個位元組。如果主裝置傳送 NACK 並在當前移出的位元組之後停止讀取,則此處請求的位元組永遠不會使用。但是,它很可能需要在下一個 I2C_SLAVE_READ_REQUEST 上再次傳送,但這在一定程度上取決於您的後端。

  • I2C_SLAVE_STOP (強制)

    “val”: 未使用

    “ret”: 始終為 0

收到停止條件。這可能隨時發生,並且後端應重置其 I2C 傳輸狀態機,以便能夠接收新請求。

軟體後端

如果您想編寫軟體後端

  • 使用標準的 i2c_driver 及其匹配機制

  • 編寫處理上述從裝置事件的 slave_callback(最好使用狀態機)

  • 透過 i2c_slave_register() 註冊此回撥

檢查 i2c-slave-eeprom 驅動程式作為示例。

匯流排驅動程式支援

如果您想向匯流排驅動程式新增從裝置支援

  • 實現用於註冊/登出從裝置的呼叫,並將這些呼叫新增到 struct i2c_algorithm 中。註冊時,您可能需要設定 I2C 從裝置地址並啟用特定於從裝置的中斷。如果使用執行時 pm,則應使用 pm_runtime_get_sync(),因為您的裝置通常需要始終上電才能檢測到其從裝置地址。登出時,執行上述操作的逆操作。

  • 捕獲從裝置中斷並將適當的 i2c_slave_events 傳送到後端。

請注意,大多數硬體都支援在同一總線上同時作為主裝置 _和_ 從裝置。因此,如果您擴充套件匯流排驅動程式,請確保該驅動程式也支援這一點。在幾乎所有情況下,從裝置支援都不需要停用主裝置功能。

檢查 i2c-rcar 驅動程式作為示例。

關於 ACK/NACK

始終 ACK 地址階段是一種良好的行為,因此主裝置可以知道裝置基本上是否存在,或者它是否神秘地消失了。使用 NACK 來宣告正忙很麻煩。SMBus 要求始終 ACK 地址階段,而 I2C 規範對此更為寬鬆。大多數 I2C 控制器也會在檢測到其從裝置地址時自動 ACK,因此沒有選擇 NACK 它們。由於這些原因,此 API 不支援地址階段中的 NACK。

目前,沒有從裝置事件來報告主裝置在從我們這裡讀取時是否 ACK 或 NACK 了某個位元組。如果需要,我們可以將其設定為可選事件。但是,這種情況應該非常罕見,因為主裝置預計之後會發送 STOP,並且我們有一個事件用於此。另外,請記住並非所有 I2C 控制器都可以報告該事件。

關於緩衝區

在此 API 的開發過程中,提出了使用緩衝區而不是僅使用位元組的問題。這種擴充套件可能是可能的,但在撰寫本文時,其有用性尚不清楚。使用緩衝區時需要記住的一些要點

  • 緩衝區應該是可選的,並且後端驅動程式將始終必須支援基於位元組的事務作為最終的回退,因為這是大多數硬體的工作方式。

  • 對於模擬硬體暫存器的後端,緩衝區在很大程度上沒有幫助,因為在寫入每個位元組後,應立即觸發一個操作。對於讀取,如果後端由於內部處理而剛更新了暫存器,則緩衝區中儲存的資料可能會過時。

  • 主裝置可以隨時傳送 STOP。對於部分傳輸的緩衝區,這意味著需要額外的程式碼來處理此異常。此類程式碼往往容易出錯。