Slave I2C


Linux I2C slave接口描述

如果使用的I2C控制器具有slave功能,那么Linux也可以成為I2C slave控制器。為此,需要總線驅動程序中的slave支持以及提供實際功能的獨立於硬件的軟件后端。后者的一個例子是slave-eeprom驅動程序,它充當雙內存驅動程序。總線上的另一個I2C master程序可以像普通EEPROM一樣訪問它,而Linux I2C slave程序可以通過sysfs訪問內容並根據需要處理數據。后端驅動程序和I2C總線驅動程序通過事件進行通信。下面是一個顯示數據流和數據傳輸方式的小圖表。虛線只標記了一個示例。后端也可以使用字符設備,只在內核中,或完全不同的東西:

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

注意:技術上,在后端和驅動程序之間還有I2C核心。然而,在撰寫本文時,該層是透明的。

使用手冊

I2C slave后端行為類似於標准I2C clients。所以,你可以像文檔' instantiating-devices '中描述的那樣實例化它們。唯一的區別是i2c slave后端有自己的地址空間。因此,您必須向最初請求的地址添加0x1000。在總線1上的7位地址0x64處實例化從用戶空間的slave-eeprom驅動程序的示例:

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

每個后端都應該有單獨的文檔來描述其特定的行為和設置。

開發人員手冊

首先,總線驅動程序和后端所使用的事件將被詳細描述。在此之后,將給出一些擴展總線驅動程序和編寫后端程序的實現提示。

I2C slave events

總線驅動程序使用以下函數向后端發送一個事件(events):

ret = i2c_slave_event(client, event, &val)

' client '描述I2C slave設備。' event '是下面描述的特殊事件類型之一。 val '為要讀/寫的數據字節保存一個u8值,因此是雙向的。即使val不用於事件,也必須始終提供指向val的指針,即不要在這里使用NULL。' ret '是后端返回的值。強制性事件必須由總線驅動程序提供,並且必須由后端驅動程序檢查。

event類型:

  • I2C_SLAVE_WRITE_REQUESTED(mandatory)

‘val’: 未使用

‘ret’: 總是 0

另一個I2C master想要向我們寫入數據。一旦檢測到我們自己的地址和寫位,就應該發送這個事件。數據還沒有到達,所以沒有需要處理或返回的內容。不過,可能需要進行喚醒或初始化。

  • I2C_SLAVE_READ_REQUESTED(mandatory)

' val ':后端返回要發送的第一個字節

‘ret’: 總是 0

另一個I2C master想從我們這里讀取數據。一旦檢測到我們自己的地址和讀位,就應該發送此事件。返回后,總線驅動程序應該發送第一個字節。

  • I2C_SLAVE_WRITE_RECEIVED(mandatory)

' val ':總線驅動發送接收的字節

' ret ': 0表示該字節被ACK,errno表示該字節被NACK

另一個I2C master發送了一個字節給我們,需要在' val '中設置。如果' ret '為零,總線驅動程序應該ack這個字節。如果' ret '是errno,則該字節應該被刪除。

  • I2C_SLAVE_READ_PROCESSED(mandatory)

' val ':后端返回要發送的下一個字節

‘ret’: 總是 0

總線驅動請求將下一個字節以' val '的形式發送給另一個I2C master。重要:這並不意味着前一個字節已經被ack了,它只是意味着前一個字節被移到了總線上! 為了確保無縫傳輸,大多數硬件在前一個字節被移出時請求下一個字節。 如果master發送NACK並在當前字節移出后停止讀取,則此處請求的字節永遠不會被使用。它很可能需要在下一個I2C_SLAVE_READ_REQUEST上再次發送,這取決於您的后端。

  • I2C_SLAVE_STOP(mandatory)

“val”:未使用的

‘ret’: 總是0

接收到停止條件。這可以隨時發生,后端應該為I2C傳輸重置它的狀態機,以便能夠接收新的請求。

Software backends

如果你想編寫一個軟件后端(software backend):

  • 使用標准的 i2c_driver 及其匹配機制
  • 寫slave_callback來處理上面的從事件(最好使用狀態機)
  • 通過i2c_slave_register()注冊這個回調

以i2c-slave-eeprom驅動為例。

Bus driver support

如果你想給總線驅動添加 slave 支持:

  • 實現注冊/注銷 slave 的調用,並將這些調用添加到結構體i2c_algorithm中。在注冊時,您可能需要設置I2C slave 地址並啟用 slave 中斷。如果你使用運行時pm,你應該使用pm_runtime_get_sync(),因為你的設備通常需要一直開機才能檢測到它的 slave 地址。當取消注冊時,執行與上面相反的操作。
  • 捕獲 slave 中斷並將適當的i2c_slave_events發送到后端。

注意,大多數硬件支持在同一總線上成為master 和 slave。所以,如果你擴展一個總線驅動程序,請確保驅動程序也支持它。在幾乎所有情況下,slave 支持不需要禁用 master 功能。

以i2c-rcar驅動為例進行說明。

關於 ACK/NACK

總是對地址階段進行ACK是一種良好的行為,這樣 master 就知道設備是基本存在還是神秘消失了。使用NACK來表示忙是很麻煩的。SMBus要求始終對地址階段進行ACK,而I2C規范在這方面更寬松。大多數I2C控制器在檢測到它們的從屬地址時也會自動進行ACK,所以沒有NACK選項。由於這些原因,這個API在地址階段不支持NACK。

目前,如果 master 在讀取數據時進行了ACK或NACK,則沒有slave事件來報告。如果有需要,我們可以將此設置為可選事件。然而,這種情況應該是極其罕見的,因為master被期望在那之后發送STOP,而我們有一個針對它的事件。另外,請記住,並非所有I2C控制器都有可能報告該事件。

關於 buffers

在開發這個API期間,出現了使用緩沖區而不僅僅是字節的問題。這樣的擴展可能是可能的,但在撰寫本文時還不清楚是否有用。使用緩沖區時要記住以下幾點:

  • 緩沖區應該是可選的,而后端驅動程序總是必須支持基於字節的事務,因為無論如何,這是大多數HW工作的方式。
  • 對於模擬硬件寄存器的后端,緩沖區在很大程度上沒有幫助,因為在每個字節寫入后,應該立即觸發一個操作。對於讀取,如果后端只是因為內部處理而更新了一個寄存器,那么保存在緩沖區中的數據可能會變得陳舊。
  • master 可以在任何時候發送STOP。對於部分傳輸的緩沖區,這意味着需要額外的代碼來處理此異常。這樣的代碼容易出錯。

Linux I2C slave EEPROM backend

這個后端在連接的I2C總線上模擬一個EEPROM。它的內存內容可以通過位於sysfs中的文件從用戶空間訪問:

/sys/bus/i2c/devices/<device-directory>/slave-eeprom

有24c02、24c32、24c64和24c512。還支持只讀變體。實例化所需的名稱形式為' slave-<type>[ro] '。例子:

24c02, read/write, address 0x64:

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

24c512, read-only, address 0x42:

 # echo slave-24c512ro 0x1042 > /sys/bus/i2c/devices/i2c-1/new_device 

如果一個名為“firmware-name”的設備屬性包含一個有效的文件名(只包含DT或ACPI),你也可以在引導期間預加載數據。

截至2015年,Linux不支持對二進制sysfs文件進行檢測,所以當另一個master文件更改內容時不會有通知。

Linux I2C slave testunit backend

這個后端可以用來觸發I2C總線 master 程序的測試用例,它需要具有某些功能的遠程設備(通常不容易獲得)。示例包括 multi-master 測試和 SMBus Host Notify測試。對於某些測試,I2C slave控制器必須能夠在master模式和slave模式之間切換,因為它也需要發送數據。

注意,這是一個用於測試和調試的設備。它不應該在生產構建中啟用。雖然有一些版本控制,並且我們努力保持向后兼容性,但並不能保證穩定的ABI !

實例化設備是常規的。例如總線0,地址0x30:

# echo “slave-testunit 0x1030” > /sys/bus/i2c/devices/i2c-0/new_device

在此之后,您將有一個write-only設備偵聽。read只會返回測試單元的8位版本號。當寫入時,設備由4個8位寄存器組成,除了一些“部分”命令,所有寄存器都必須被寫入以啟動一個測試用例,也就是說,你通常向設備寫入4個字節。寄存器是:

  0x00 CMD - 觸發哪個測試;

  0x01 DATAL - 配置字節1;

  0x02 DATAH - 配置字節2;

  0x03 DELAY - 延遲n * 10ms,直到測試開始。

使用i2c-tools包中的' i2cset ',通用命令看起來像:

# i2cset -y <bus_num> <testunit_address> <CMD> <DATAL> <DATAH> <DELAY> i

DELAY是一個通用參數,用於在CMD中延遲測試的執行。當一個命令正在運行時(包括延遲),新的命令將不會被確認(即NACK)。你需要等待,直到舊的完成。

下面的部分將描述這些命令。無效的命令將導致傳輸不被確認(NACK)。

Commands

0x00 NOOP(預留將來使用)

0x01 READ_BYTES (也需要master模式)

  DATAL - 讀取數據的地址(低7位,最高位目前未使用);

  DATAH -讀取的字節數

對於測試總線master驅動程序是否正確地處理multi-master非常有用。您可以觸發測試單元從總線上的另一個設備讀取字節。如果被測試的總線master也想同時訪問總線,總線將會繁忙。從設備0x50讀取128字節,延遲50ms的例子:

# i2cset -y 0 0x30 0x01 0x50 0x80 0x05 i

0x02 SMBUS_HOST_NOTIFY (也需要master模式)

  DATAL - 要發送的狀態字的低字節;

  DATAH - 要發送的狀態字的高字節

此測試將向主機發送SMBUS_HOST_NOTIFY消息。注意,在Linux內核中,status字目前被忽略。在10ms后發送通知的例子:

# i2cset -y 0 0x30 0x02 0x42 0x64 0x01 i

0x03 SMBUS_BLOCK_PROC_CALL(部分命令)

  DATAL - 必須為' 1 ',即再寫一個字節;

  DATAH - 返回的字節數;

  DELAY - 不適用,部分命令!

這個測試將響應SMBus規范定義的塊進程調用。寫入的一個數據字節指定在接下來的讀傳輸中將被送回多少字節。注意,在這個讀傳輸中,testunit將為后面的字節長度加上前綴。因此,如果您的master總線驅動程序像大多數程序一樣模擬SMBus調用,那么它需要支持i2c_msg的I2C_M_RECV_LEN標志。這是一個很好的測試用例。返回的數據首先包含長度,然后是length-1到0的字節數組。下面是一個使用i2ctransfer(你需要i2c-tools v4.2或更高版本)模擬i2c_smbus_block_process_call()的示例:

# i2ctransfer -y 0 w3@0x30 0x03 0x01 0x10 r?
0x10 0x0f 0x0e 0x0d 0x0c 0x0b 0x0a 0x09 0x08 0x07 0x06 0x05 0x04 0x03 0x02 0x01 0x00

 返回的第一個字節0x10代表接下來要返回的總字節數,即16個字節:0x0f 0x0e 0x0d 0x0c 0x0b 0x0a 0x09 0x08 0x07 0x06 0x05 0x04 0x03 0x02 0x01 0x00


免責聲明!

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



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