天天看點

Slave I2C

Linux I2C slave接口描述

如果使用的I2C控制器具有slave功能,那麼Linux也可以成為I2C slave控制器。為此,需要總線驅動程式中的slave支援以及提供實際功能的獨立于硬體的軟體後端。後者的一個例子是slave-eeprom驅動程式,它充當雙記憶體驅動程式。總線上的另一個I2C master程式可以像普通EEPROM一樣通路它,而Linux I2C slave程式可以通過sysfs通路内容并根據需要處理資料。後端驅動程式和I2C總線驅動程式通過事件進行通信。下面是一個顯示資料流和資料傳輸方式的小圖表。虛線隻标記了一個示例。後端也可以使用字元裝置,隻在核心中,或完全不同的東西:

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

I2C slave後端行為類似于标準I2C clients。是以,你可以像文檔' instantiating-devices '中描述的那樣執行個體化它們。唯一的差別是i2c slave後端有自己的位址空間。是以,您必須向最初請求的位址添加0x1000。在總線1上的7位位址0x64處執行個體化從使用者空間的slave-eeprom驅動程式的示例:

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

首先,總線驅動程式和後端所使用的事件将被較長的描述。在此之後,将給出一些擴充總線驅動程式和編寫後端程式的實作提示。

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

' 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 ':後端傳回要發送的第一個位元組

另一個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 ':後端傳回要發送的下一個位元組

總線驅動請求将下一個位元組以' val '的形式發送給另一個I2C master。重要:這并不意味着前一個位元組已經被ack了,它隻是意味着前一個位元組被移到了總線上! 為了確定無縫傳輸,大多數硬體在前一個位元組被移出時請求下一個位元組。 如果master發送NACK并在目前位元組移出後停止讀取,則此處請求的位元組永遠不會被使用。它很可能需要在下一個I2C_SLAVE_READ_REQUEST上再次發送,這取決于您的後端。

I2C_SLAVE_STOP(mandatory)

“val”:未使用的

‘ret’: 總是0

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

如果你想編寫一個軟體後端(software backend):

使用标準的 i2c_driver 及其比對機制

寫slave_callback來處理上面的從事件(最好使用狀态機)

通過i2c_slave_register()注冊這個回調

以i2c-slave-eeprom驅動為例。

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

實作注冊/登出 slave 的調用,并将這些調用添加到結構體i2c_algorithm中。在注冊時,您可能需要設定I2C slave 位址并啟用 slave 中斷。如果你使用運作時pm,你應該使用pm_runtime_get_sync(),因為你的裝置通常需要一直開機才能檢測到它的 slave 位址。當取消注冊時,執行與上面相反的操作。

捕獲 slave 中斷并将适當的i2c_slave_events發送到後端。

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

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

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

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

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

緩沖區應該是可選的,而後端驅動程式總是必須支援基于位元組的事務,因為無論如何,這是大多數HW工作的方式。

對于模拟硬體寄存器的後端,緩沖區在很大程度上沒有幫助,因為在每個位元組寫入後,應該立即觸發一個操作。對于讀取,如果後端隻是因為内部處理而更新了一個寄存器,那麼儲存在緩沖區中的資料可能會變得陳舊。

master 可以在任何時候發送STOP。對于部分傳輸的緩沖區,這意味着需要額外的代碼來處理此異常。這樣的代碼容易出錯。

Linux I2C slave EEPROM backend

這個後端在連接配接的I2C總線上模拟一個EEPROM。它的記憶體内容可以通過位于sysfs中的檔案從使用者空間通路:

有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:

在此之後,您将有一個write-only裝置偵聽。read隻會傳回測試單元的8位版本号。當寫入時,裝置由4個8位寄存器組成,除了一些“部分”指令,所有寄存器都必須被寫入以啟動一個測試用例,也就是說,你通常向裝置寫入4個位元組。寄存器是:

  0x00 CMD - 觸發哪個測試;

  0x01 DATAL - 配置位元組1;

  0x02 DATAH - 配置位元組2;

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

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

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

下面的部分将描述這些指令。無效的指令将導緻傳輸不被确認(NACK)。

0x00 NOOP(預留将來使用)

0x01 READ_BYTES (也需要master模式)

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

  DATAH -讀取的位元組數

對于測試總線master驅動程式是否正确地處理multi-master非常有用。您可以觸發測試單元從總線上的另一個裝置讀取位元組。如果被測試的總線master也想同時通路總線,總線将會繁忙。從裝置0x50讀取128位元組,延遲50ms的例子:

0x02 SMBUS_HOST_NOTIFY (也需要master模式)

  DATAL - 要發送的狀态字的低位元組;

  DATAH - 要發送的狀态字的高位元組

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

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()的示例:

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

繼續閱讀