[ 導讀] 本文經過閱讀內核代碼,來梳理一下I2C子系統的總體視圖。在開發I2C設備驅動程序時,每每缺少對於系統總體的認識,致使沒有一個清晰的思路。因此從高層級來分析一下I2C系統的設計思路,將有助於設計調試具體的驅動程序。html
I2C是一種芯片間通信總線技術,最先由Philips設計制定。下面內容參考I2C 2.1 規格書linux
半雙工通訊方式,通訊採用主/從結構算法
支持多主模式,下圖來源於I2C 2.0規格書
編程
其內部電氣實現採用集電極開路(Open-collector)/漏極開路(open-drain)結構以實現線與功能,這是總線的實現基礎,多芯片經過查詢總線狀態實現介質仲裁以實現總線控制。微信
SMBUS(system management bus) 。 大多數SMBus系統也符合I2C,電氣約束對於SMBus更爲嚴格,而且它標準化了特定的協議消息和習慣用語。 支持I2C的控制器也能夠支持大多數SMBus操做,可是SMBus控制器並不支持I2C控制器將支持的全部協議選項。 經過使用I2C原語或經過向不支持這些I2C操做的i2c_adapter設備發出SMBus命令,能夠執行各類SMBus協議操做。數據結構
I2C bus(Inter-Integrated Circuit bus) https://www.i2c-bus.org/框架
PC體系中經過橋接芯片,擴展出PCI,在由PCI擴展出I2C適配器,進而獲得I2C總線,或者橋接芯片直接擴展出SMBUS/I2C總線。ide
嵌入式應用中,則可能更多的狀況是處理器內置了I2C/SMBUS總線控制器,直接可獲得I2C/SMBUS總線。嵌入式系統中經常會設計不少傳感器掛載在I2C總線上,好比溫度檢測,壓力檢測等等,又或者諸如電容觸摸屏、電源管理IC等等。函數
I2C子系統的主要目的是,對I2C總線以及設備利用面向對象編程思想實現統一建模,以高內聚-低耦合的軟件工程思想,實現一個分層體系結構,以便於內核統一管理I2C設備,從而能夠更容易的在linux下實現I2C設備以及高可移植。
其內部有幾個關鍵數據結構,來梳理一下:
該結構體主要用於板級I2C信息管理
該結構體主要用於抽象I2C報文,其內容以下:
主要用於抽象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);
該結構體主要用於抽象I2C 設備的ID屬性,經過內核接口函數i2c_get_device_id以獲取設備ID屬性。
Linux I2C編程接口支持總線交互的主端和從端。從高層級看由兩種驅動程序和兩種設備構成:
適配器設備與適配器設備驅動對:I2C 適配器驅動程序用於抽象控制器硬件;它綁定到一個物理設備(多是一個PCI設備(PC體系多一些)或platform_device(嵌入式應用居多)),並構建i2c_adapter實體以呈現所管理的1個I2C總線段。
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);
/* 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;
圖片來源:https://www.kernel.org/doc/html/latest/i2c/slave-interface.html
主端總線驅動職責:
從端設備驅動職責:
當用戶程序發出文件操做申請I2C事務時:
從應用程序直到底層的大體交互流程以下:
I2C總線子系統在Linux內核中I2C總線模型分爲主/從兩端,主端主要有適配器以及適配器驅動負責管理總線,從端主要有從設備抽象以及設備驅動實現具體的從設備應用。主端適配器以兩種形式存在於內核代碼PCI橋接適配器或者platform_device形式。從整體理解I2C子系統的驅動模型,以及相應主要數據結構之間的關係,將有助於開發調試驅動程序,快速定位問題。
文章出自微信公衆號:嵌入式客棧,因爲時間關係,博客可能沒法及時更新,最新內容,請關注本人公衆號,嚴禁商業使用,違法必究