如何例項化 I2C 裝置¶
與 PCI 或 USB 裝置不同,I2C 裝置不會在硬體級別進行列舉。 相反,軟體必須知道哪些裝置連線到每個 I2C 匯流排段,以及這些裝置正在使用的地址。 因此,核心程式碼必須顯式地例項化 I2C 裝置。 有幾種方法可以實現這一點,具體取決於上下文和要求。
方法 1:靜態宣告 I2C 裝置¶
當 I2C 匯流排是系統匯流排時,此方法適用,許多嵌入式系統就是這種情況。 在這種系統上,每個 I2C 匯流排都有一個預先知道的編號。 因此,可以預先宣告位於此總線上的 I2C 裝置。
在不同的體系結構上,此資訊以不同的方式提供給核心:裝置樹、ACPI 或板檔案。
註冊有問題的 I2C 匯流排時,i2c-core 會自動例項化 I2C 裝置。 當 I2C 裝置所在的匯流排消失時(如果發生過),這些裝置將自動解除繫結並銷燬。
透過裝置樹宣告 I2C 裝置¶
在使用裝置樹的平臺上,I2C 裝置的宣告在主控制器的子節點中完成。
示例
i2c1: i2c@400a0000 {
/* ... master properties skipped ... */
clock-frequency = <100000>;
flash@50 {
compatible = "atmel,24c256";
reg = <0x50>;
};
pca9532: gpio@60 {
compatible = "nxp,pca9532";
gpio-controller;
#gpio-cells = <2>;
reg = <0x60>;
};
};
在此,兩個裝置以 100kHz 的速度連線到匯流排。 有關設定裝置可能需要的其他屬性,請參閱 Documentation/devicetree/bindings/ 中的裝置樹文件。
透過 ACPI 宣告 I2C 裝置¶
ACPI 還可以描述 I2C 裝置。 此處有特殊的文件,當前位於 基於 ACPI 的裝置列舉。
在板檔案中宣告 I2C 裝置¶
在許多嵌入式體系結構中,裝置樹已取代了基於板檔案的舊硬體描述,但後者仍用於舊程式碼中。 透過板檔案例項化 I2C 裝置是透過 struct i2c_board_info 陣列完成的,該陣列透過呼叫 i2c_register_board_info() 註冊。
示例(來自 omap2 h4)
static struct i2c_board_info h4_i2c_board_info[] __initdata = {
{
I2C_BOARD_INFO("isp1301_omap", 0x2d),
.irq = OMAP_GPIO_IRQ(125),
},
{ /* EEPROM on mainboard */
I2C_BOARD_INFO("24c01", 0x52),
.platform_data = &m24c01,
},
{ /* EEPROM on cpu card */
I2C_BOARD_INFO("24c01", 0x57),
.platform_data = &m24c01,
},
};
static void __init omap_h4_init(void)
{
(...)
i2c_register_board_info(1, h4_i2c_board_info,
ARRAY_SIZE(h4_i2c_board_info));
(...)
}
上面的程式碼在 I2C 匯流排 1 上聲明瞭 3 個裝置,包括它們的各自地址和驅動程式所需的自定義資料。
方法 2:顯式例項化裝置¶
當更大的裝置使用 I2C 匯流排進行內部通訊時,此方法適用。 一個典型的例子是電視介面卡。 它們可以具有調諧器、影片解碼器、音訊解碼器等,通常透過 I2C 匯流排連線到主晶片。 您不會預先知道 I2C 匯流排的編號,因此無法使用上面描述的方法 1。 相反,您可以顯式例項化 I2C 裝置。 這是透過填充 struct i2c_board_info 並呼叫 i2c_new_client_device() 來完成的。
示例(來自 sfe4001 網路驅動程式)
static struct i2c_board_info sfe4001_hwmon_info = {
I2C_BOARD_INFO("max6647", 0x4e),
};
int sfe4001_init(struct efx_nic *efx)
{
(...)
efx->board_info.hwmon_client =
i2c_new_client_device(&efx->i2c_adap, &sfe4001_hwmon_info);
(...)
}
上面的程式碼在有問題的網路介面卡上的 I2C 總線上例項化 1 個 I2C 裝置。
另一種變體是,您不確定 I2C 裝置是否存在(例如,對於廉價版本的板上不存在的可選功能,但您無法區分它們),或者它可能具有不同的地址從一個板到下一個板(製造商在沒有通知的情況下更改其設計)。 在這種情況下,您可以呼叫 i2c_new_scanned_device() 而不是 i2c_new_client_device()。
示例(來自 nxp OHCI 驅動程式)
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
static int usb_hcd_nxp_probe(struct platform_device *pdev)
{
(...)
struct i2c_adapter *i2c_adap;
struct i2c_board_info i2c_info;
(...)
i2c_adap = i2c_get_adapter(2);
memset(&i2c_info, 0, sizeof(struct i2c_board_info));
strscpy(i2c_info.type, "isp1301_nxp", sizeof(i2c_info.type));
isp1301_i2c_client = i2c_new_scanned_device(i2c_adap, &i2c_info,
normal_i2c, NULL);
i2c_put_adapter(i2c_adap);
(...)
}
上面的程式碼在有問題的 OHCI 介面卡上的 I2C 總線上例項化最多 1 個 I2C 裝置。 它首先嚐試地址 0x2c,如果沒有找到任何東西,則嘗試地址 0x2d,如果仍然沒有找到任何東西,則只需放棄。
例項化 I2C 裝置的驅動程式負責在清理時銷燬它。 這是透過對先前由 i2c_new_client_device() 或 i2c_new_scanned_device() 返回的指標呼叫 i2c_unregister_device() 來完成的。
方法 3:探測 I2C 總線上的某些裝置¶
有時您沒有關於 I2C 裝置的足夠資訊,甚至無法呼叫 i2c_new_scanned_device()。 典型的情況是 PC 主機板上的硬體監控晶片。 有幾十種型號,可以位於 25 個不同的地址。 鑑於那裡有大量的主機板,因此幾乎不可能構建正在使用的硬體監控晶片的詳盡列表。 幸運的是,這些晶片中的大多數都具有製造商和裝置 ID 暫存器,因此可以透過探測來識別它們。
在這種情況下,I2C 裝置既未宣告也未顯式例項化。 相反,i2c-core 將在載入驅動程式後立即探測此類裝置,如果找到任何裝置,則會自動例項化一個 I2C 裝置。 為了防止此機制發生任何異常行為,請應用以下限制
I2C 裝置驅動程式必須實現 detect() 方法,該方法透過從任意暫存器讀取來識別受支援的裝置。
只會探測可能具有受支援裝置並同意被探測的匯流排。 例如,這避免了在電視介面卡上探測硬體監控晶片。
示例:請參見 drivers/hwmon/lm90.c 中的 lm90_driver 和 lm90_detect()
由此類成功探測而例項化的 I2C 裝置將在檢測到它們的驅動程式被移除或底層 I2C 匯流排本身被銷燬時自動銷燬,以先發生者為準。
那些熟悉 2.4 核心和早期 2.6 核心的 I2C 子系統的人會發現此方法 3 本質上與當時所做的事情相似。 兩個顯著的區別是
探測現在只是例項化 I2C 裝置的一種方式,而當時是唯一的方式。 如果可能,應首選方法 1 和 2。 方法 3 僅應在沒有其他方法時使用,因為它可能具有不良的副作用。
I2C 匯流排現在必須明確說明哪些 I2C 驅動程式類可以探測它們(透過類位欄位),而當時預設探測所有 I2C 匯流排。 預設值是一個空類,這意味著不發生探測。 類位欄位的目的是限制上述不良的副作用。
再次,應儘可能避免方法 3。 顯式裝置例項化(方法 1 和 2)是更安全、更快速的首選方法。
方法 4:從使用者空間例項化¶
通常,核心應該知道哪些 I2C 裝置已連線以及它們所在的地址。 但是,在某些情況下,它不知道,因此添加了一個 sysfs 介面,以使使用者提供資訊。 此介面由 2 個屬性檔案組成,這些檔案在每個 I2C 匯流排目錄中建立:new_device 和 delete_device。 這兩個檔案都是隻寫的,您必須將正確的引數寫入其中才能正確地例項化和分別刪除 I2C 裝置。
檔案 new_device 採用 2 個引數:I2C 裝置的名稱(一個字串)和 I2C 裝置的地址(一個數字,通常以十六進位制表示,以 0x 開頭,但也可以用十進位制表示)。
檔案 delete_device 採用一個引數:I2C 裝置的地址。 由於在給定的 I2C 段上沒有兩個裝置可以位於同一地址,因此該地址足以唯一地標識要刪除的裝置。
示例
# echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device
儘管此介面應僅在無法進行核心裝置宣告時使用,但在許多情況下它可能會有所幫助
I2C 驅動程式通常檢測裝置(上面的方法 3),但是您的裝置所在的匯流排段沒有設定正確的類位,因此檢測不會觸發。
I2C 驅動程式通常檢測裝置,但是您的裝置位於意外的地址。
I2C 驅動程式通常檢測裝置,但是您的裝置未被檢測到,這可能是因為檢測例程過於嚴格,或者因為您的裝置尚未獲得官方支援,但您知道它是相容的。
您正在測試板上開發驅動程式,您自己在該板上焊接了 I2C 裝置。
此介面是某些 I2C 驅動程式實現的 force_* 模組引數的替代品。 由於它是在 i2c-core 中而不是在每個裝置驅動程式中單獨實現,因此效率更高,並且還具有不必重新載入驅動程式即可更改設定的優點。 您還可以在載入驅動程式之前甚至沒有驅動程式的情況下例項化該裝置,並且您無需知道該裝置需要什麼驅動程式。