[ 導讀] 本文透過閱讀核心程式碼,來梳理一下I2C子系統的整體檢視。在開發I2C裝置驅動程式時,往往缺乏對於系統整體的認識,導致沒有一個清晰的思路。所以從高層級來分析一下I2C系統的設計思路,將有助於設計除錯具體的驅動程式。

I2C/SMBUS基礎

I2C是一種晶片間通訊匯流排技術,最早由Philips設計制定。下面內容參考I2C 2。1 規格書

半雙工通訊方式,通訊採用主/從結構

支援多主模式,下圖來源於I2C 2。0規格書

Linux中I2C子系統到底長啥樣?

其內部電氣實現採用集電極開路(Open-collector)/漏極開路(open-drain)結構以實現線與功能,這是匯流排的實現基礎,多晶片透過查詢匯流排狀態實現介質仲裁以實現匯流排控制。

Linux中I2C子系統到底長啥樣?

匯流排訊號由兩線實現,序列時鐘線SCL(Serial Clock Line)/序列資料線SDA(serial Data Line)。

具有三種通訊速率模式:

standard mode:0-100 kbps (bps: bit/s)

Fast mode:0-400 kbps

High-speed mode : 0-3。4Mbps

可支援混速模式

Linux中I2C子系統到底長啥樣?

不同的速率在硬體設計時需要注意訊號的完整性,I2C匯流排等效電容cx

支援7bit/10bit 兩種晶片地址模式

I2C匯流排電氣特性,這個非常重要,須嚴格遵守標準的電氣特性

Linux中I2C子系統到底長啥樣?

Linux中I2C子系統到底長啥樣?

SMBUS(system management bus) 。 大多數SMBus系統也符合I2C,電氣約束對於SMBus更為嚴格,並且它標準化了特定的協議訊息和習慣用語。 支援I2C的控制器也可以支援大多數SMBus操作,但是SMBus控制器並不支援I2C控制器將支援的所有協議選項。 透過使用I2C原語或透過向不支援這些I2C操作的i2c_adapter裝置發出SMBus命令,可以執行各種SMBus協議操作。

http://

smbus。org/

I2C bus(Inter-Integrated Circuit bus)

https://www。

i2c-bus。org/

I2C 在Linux裝置中的拓撲結構

在PC體系中,大體如下拓撲:

Linux中I2C子系統到底長啥樣?

PC體系中透過橋接晶片,擴展出PCI,在由PCI擴展出I2C介面卡,進而得到I2C匯流排,或者橋接晶片直接擴展出SMBUS/I2C匯流排。

在嵌入式應用中,則可能為:

Linux中I2C子系統到底長啥樣?

嵌入式應用中,則可能更多的情況是處理器內建了I2C/SMBUS匯流排控制器,直接可得到I2C/SMBUS匯流排。嵌入式系統中常常會設計很多感測器掛載在I2C總線上,比如溫度檢測,壓力檢測等等,又或者諸如電容觸控式螢幕、電源管理IC等等。

程式碼實現

I2C 的core實現位於。/drivers/i2c/下,實現了I2C匯流排裝置以及驅動(介面卡)和裝置驅動的註冊、登出方法,I2C通訊方法algorithm抽象,以及與具體硬體無關的程式碼

I2C主控制器驅動位於 。/drivers/i2c/busses/,這裡主要實現匯流排控制器,具體體現為i2c_adapter的實現。負責I2C介面卡與從裝置通訊。I2C匯流排驅動由i2c_adapter和i2c_algorithm來抽象描述。

裝置驅動則分散在。/driver/下,這取決於具體的實現,種類繁多。

i2c-dev,大多位於drivers/i2c/i2c-dev。c,這種方法只是封裝了主機(I2Cmaster,一般是SoC中內建的I2C控制器)的I2C基本操作,並且嚮應用層提供相應的操作介面,應用層程式碼需要自己去實現對slave的控制和操作,所以這種I2C驅動相當於提供給應用層可以訪問slave硬體裝置的介面,本身並未對硬體做任何操作,應用需要實現對硬體的操作。這種模式也稱為應用驅動程式。

另一種I2C驅動是將所有的程式碼都放在驅動層實現,直接嚮應用層提供最終結果。應用層甚至不需要知道這裡面有I2C存在,譬如電容式觸控式螢幕驅動,直接嚮應用層提供/dev/input/event1的操作介面,應用層程式設計的人根本不知道event1中涉及到了I2C。

Linux中I2C子系統到底長啥樣?

I2C子系統的主要目的是,對I2C匯流排以及裝置利用面向物件程式設計思想實現統一建模,以高內聚-低耦合的軟體工程思想,實現一個分層體系結構,以便於核心統一管理I2C裝置,從而可以更容易的在linux下實現I2C裝置以及高可移植。

主要資料結構

其內部有幾個關鍵資料結構,來梳理一下:

i2c_client, 用於抽象掛載在I2C總線上的從裝置

i2c_driver,用於驅動掛載在I2C匯流排生的從裝置,也即從裝置的裝置驅動程式

i2c_adapter,用於抽象I2C的主裝置

i2c_algorithm,抽象I2C匯流排操作介面

Linux中I2C子系統到底長啥樣?

i2c_devinfo

該結構體主要用於板級I2C資訊管理

Linux中I2C子系統到底長啥樣?

i2c_msg

該結構體主要用於抽象I2C報文,其內容如下:

Linux中I2C子系統到底長啥樣?

i2c_timings

主要用於抽象I2C電氣特性,對於支援裝置樹的系統構建而言,主要透過以下核心介面函式,從裝置樹解析電氣特性引數。

void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults)

{

int ret;

memset(t, 0, sizeof(*t));

ret = device_property_read_u32(dev, “clock-frequency”, &t->bus_freq_hz);

if (ret && use_defaults)

t->bus_freq_hz = 100000;

ret = device_property_read_u32(dev, “i2c-scl-rising-time-ns”, &t->scl_rise_ns);

if (ret && use_defaults) {

if (t->bus_freq_hz <= 100000)

t->scl_rise_ns = 1000;

else if (t->bus_freq_hz <= 400000)

t->scl_rise_ns = 300;

else

t->scl_rise_ns = 120;

}

ret = device_property_read_u32(dev, “i2c-scl-falling-time-ns”, &t->scl_fall_ns);

if (ret && use_defaults) {

if (t->bus_freq_hz <= 400000)

t->scl_fall_ns = 300;

else

t->scl_fall_ns = 120;

}

device_property_read_u32(dev, “i2c-scl-internal-delay-ns”, &t->scl_int_delay_ns);

ret = device_property_read_u32(dev, “i2c-sda-falling-time-ns”, &t->sda_fall_ns);

if (ret && use_defaults)

t->sda_fall_ns = t->scl_fall_ns;

device_property_read_u32(dev, “i2c-sda-hold-time-ns”, &t->sda_hold_ns);

}

EXPORT_SYMBOL_GPL(i2c_parse_fw_timings);

Linux中I2C子系統到底長啥樣?

i2c_device_identity

該結構體主要用於抽象I2C 裝置的ID屬性,透過核心介面函式i2c_get_device_id以獲取裝置ID屬性。

Linux中I2C子系統到底長啥樣?

總體框架

概述

Linux I2C程式設計介面支援匯流排互動的主端和從端。從高層級看由兩種驅動程式和兩種裝置構成:

介面卡裝置與介面卡裝置驅動對

:I2C 介面卡驅動程式用於抽象控制器硬體;它繫結到一個物理裝置(可能是一個PCI裝置(PC體系多一些)或platform_device(嵌入式應用居多)),並構建i2c_adapter實體以呈現所管理的1個I2C匯流排段。

platform_device。 比如:i2c-s3c2410,如下:

static const struct platform_device_id s3c24xx_driver_ids[] = {

{

。name = “s3c2410-i2c”,

。driver_data = 0,

}, {

。name = “s3c2440-i2c”,

。driver_data = QUIRK_S3C2440,

}, {

。name = “s3c2440-hdmiphy-i2c”,

。driver_data = QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO,

}, { },

};

MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);

pci-I2C 介面卡裝置。如在i2c-ali1535。c中:

/* ALI1535 device address register bits */

#define ALI1535_RD_ADDR 0x01 /* Read/Write Bit in Device */

/* Address field */

/* -> Write = 0 */

/* -> Read = 1 */

#define ALI1535_SMBIO_EN 0x04 /* SMB I/O Space enable */

/*PCI 裝置驅動*/

static struct pci_driver ali1535_driver;

static unsigned long ali1535_smba;

static unsigned short ali1535_offset;

I2C從裝置及裝置驅動對

:每個I2C匯流排段上將有一個由結構i2c_client表示的I2C裝置。這些裝置將被繫結到一個struct i2c_driver,遵循標準的Linux驅動程式模型。

架構

圖片來源:

https://www。

kernel。org/doc/html/lat

est/i2c/slave-interface。html

Linux中I2C子系統到底長啥樣?

主端匯流排驅動職責:

介面卡和演算法驅動程式,見下面i2c_algorithm結構體詳細描述

管理I2C匯流排互動

從端裝置驅動職責:

i2c_client結構體具有裝置的I2C匯流排地址以及介面卡的驅動程式指標

當用戶程式發出檔案操作申請I2C事務時

i2C_transfer (i2C-core。c) 呼叫 adap_algo_master_xfer,資料或訊息以i2c_msg結構體傳入。

介面卡對硬體I / O地址進行讀/寫操作,實現底層的I2C讀寫裝置操作。

從應用程式直到底層的大致互動流程如下:

Linux中I2C子系統到底長啥樣?

總結一下

I2C匯流排子系統在Linux核心中I2C匯流排模型分為主/從兩端,主端主要有介面卡以及介面卡驅動負責管理匯流排,從端主要有從裝置抽象以及裝置驅動實現具體的從裝置應用。主端介面卡以兩種形式存在於核心程式碼PCI橋接介面卡或者platform_device形式。從總體理解I2C子系統的驅動模型,以及相應主要資料結構之間的關係,將有助於開發除錯驅動程式,快速定位問題。

如果覺得本文有價值,不妨轉發給需要的小夥伴們。

版權宣告:所有文章版權歸嵌入式客棧所有,如商業使用,須嵌入式客棧授權。

歡迎關注微信公眾號,內容更豐富。

Linux中I2C子系統到底長啥樣?