1 I2C子系統框架 Linux I2C子系統分成三部分:I2C核心層、I2C匯流排驅動和I2C設備驅動。 (1)I2C核心層 I2C核心提供了I2C匯流排驅動和設備驅動的註冊、註銷方法,I2C通信方法(即algorithm)上層的與具體適配器無關的代碼以及探測設備、檢測設備地址的上層代碼等。核心層的代 ...
1 I2C子系統框架
Linux I2C子系統分成三部分:I2C核心層、I2C匯流排驅動和I2C設備驅動。
(1)I2C核心層
I2C核心提供了I2C匯流排驅動和設備驅動的註冊、註銷方法,I2C通信方法(即algorithm)上層的與具體適配器無關的代碼以及探測設備、檢測設備地址的上層代碼等。核心層的代碼在drivers/i2c/i2c-core.c中實現。
(2)I2C匯流排驅動
I2C匯流排驅動是對I2C硬體適配器端的實現,適配器可由CPU控制。I2C匯流排驅動主要包含了I2C適配器數據結構i2c_adapter、I2C適配器的Algorithm數據結構i2c_algorithm和控制I2C適配器產生通信信號的函數。
經由I2C匯流排驅動的代碼,我們可以控制I2C適配器以主控產生開始位、停止位、讀寫周期,以及以從設備方式被讀寫、產生ACK等。
Linux在drivers/i2c/下建立了busses目錄用於存放各種已實現的I2C匯流排驅動,其中包括samsung S3C系列晶元的I2C匯流排驅動實現i2c-s3c2410.c。
(3)I2C設備驅動
I2C設備驅動是對I2C硬體設備端的實現,設備一般掛接在受CPU控制的I2C控制器上,通過I2C適配器與CPU交換數據。I2C設備驅動主要包含數據結構i2c_driver和i2c_client,我們需要根據具體設備實現其中的成員函數。
【溫馨提示】i2c-dev.c實現了I2C適配器設備文件的功能,每一個I2C適配器都被分配一個設備。通過適配器訪問設備時的主設備號都為89,次設備號為0~255。i2c-dev.c並不是針對特定的設備而設計的,只是提供了通用的read()、write()和ioctl()等介面,應用層可以借用這些介面訪問掛接在適配器上的I2C設備的存儲空間或者寄存器,並控制I2C設備的工作方式。
2 I2C子系統數據結構
(1)i2c_adapter結構
I2C子系統用struct i2c_adapter來描述一個物理的適配器,適配器的具體通信方法由struct i2c_adapter的一個類型為struct i2c_algorithm的成員來描述。定義在include/linux/i2c.h。
1 struct i2c_adapter { 2 struct module *owner; 3 unsigned int id; // 適配器ID,這個在適配器驅動中不常用 4 unsigned int class; // 適配器的類類型 5 const struct i2c_algorithm *algo; // 指向通信方法數據的指針 6 void *algo_data; 7 8 /* data fields that are valid for all devices */ 9 u8 level; /* nesting level for lockdep */ 10 struct mutex bus_lock; 11 12 int timeout; // 傳輸超時時間 13 int retries; // 重試次數 14 struct device dev; // 內嵌的device結構 15 16 int nr; // 匯流排編號(也是適配器編號),同時對應設備節點/dev/i2c-x(x=0,1,2...)來訪問 17 char name[48]; // 適配器名稱,這個名稱可以通過/sys/bus/i2c/devices/i2c-x/name(x=0,1,2...)來訪問 18 struct completion dev_released; 19 };
(2)i2c_algorithm結構
struct i2c_adapter中用於描述通信方法的成員i2c_algorithm定義如下,該成員及其內核方法的實現是開發I2C匯流排驅動的核心任務。此結構體定義在include/linux/i2c.h。
1 struct i2c_algorithm { 2 /* 指向具體的I2C傳輸函數的指針,對應的傳輸一般會通過直接操作適配器硬體來發起。這個 3 函數的傳入參數分別是使用該傳輸方法的適配器adap,待傳輸的消息msgs和消息數量num。 */ 4 int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, 5 int num); 6 /* 指向具體的SMBus傳輸函數的指針。SMBus協議大部分基於I2C匯流排規範,併在基礎上做了擴展,在訪問 7 時序上由一些差異。如果這個指針置NULL,基於SMBus協議的通信將通過I2C傳輸來模擬 */ 8 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, 9 unsigned short flags, char read_write, 10 u8 command, int size, union i2c_smbus_data *data); 11 12 /* 指向返回適配器支持功能的函數的指針,這些功能定義在incude/linux/i2c.h中以I2C_FUNC開頭 13 的巨集表示,常用的有I2C_FUNC_I2C、I2C_FUNC_SMBUS_EMUL、I2C_FUNC_PROTOCOL_MANGLING */ 14 u32 (*functionality) (struct i2c_adapter *); 15 };
(3)i2c_msg結構
i2c_msg結構用來描述一個用於傳輸I2C的消息。此結構體定義在include/linux/i2c.h。
1 struct i2c_msg { 2 __u16 addr; // 從機地址 3 __u16 flags; // 反映消息特定的標誌,當I2C_M_RD位被設置時,表示消息方向是主機從從機去讀,如果 4 // 不設置該位(0),則消息方向是主機向從機寫入。I2C_M_RD可以被所有適配器處理。I2C_M_NOSTART等 5 // 幾個消息則需要適配器支持I2C_FUNC_PROTOCOL_MANGLING功能。 6 #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ 7 #define I2C_M_RD 0x0001 /* read data, from slave to master */ 8 #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */ 9 #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ 10 #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ 11 #define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ 12 #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ 13 __u16 len; // 消息數據長度,單位是位元組 14 __u8 *buf; // 指向存放消息數據緩衝區 15 };
一個i2c_msg對象代表一個底層I2C傳輸單元。在驅動程式中,i2c_msg對象通過函數i2c_transfer()進行處理,而i2c_transfer()則調用適配器通信方法的master_xfer()來傳輸消息數據。
(4)i2c_driver結構
i2c_driver結構定義在include/linux/i2c.h中。它對應於一套驅動方法,其主要成員函數是probe()、remove()、suspend()、resume()等,另外,struct i2c_device_id形式的id_table是該驅動所支持的I2C設備的ID表。
1 struct i2c_driver { 2 unsigned int class; 3 4 /* 分別是依附和脫離i2c_adapter的函數指針,驅動註冊函數會遍歷適配器設備類i2c_adapter_class中的所有設備 5 並調用該驅動的attach_adapter方法進行依附。相應的,在添加i2c_adapter時,適配器註冊函數會遍歷匯流排i2c_bus_type 6 上所有的驅動,如果驅動定義了attach_adapter方法,他也將得到調用 */ 7 int (*attach_adapter)(struct i2c_adapter *) __deprecated; 8 int (*detach_adapter)(struct i2c_adapter *) __deprecated; 9 10 /* 在設備驅動中,當匯流排i2c_bus_type上的設備與設備驅動匹配後被調用。 */ 11 int (*probe)(struct i2c_client *, const struct i2c_device_id *); 12 int (*remove)(struct i2c_client *); 13 14 /* driver model interfaces that don't relate to enumeration */ 15 void (*shutdown)(struct i2c_client *); 16 int (*suspend)(struct i2c_client *, pm_message_t mesg); 17 int (*resume)(struct i2c_client *); 18 19 void (*alert)(struct i2c_client *, unsigned int data); 20 21 int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); 22 23 /* 內嵌的driver結構。在註冊i2c_driver對象時,i2c_driver->driver的匯流排類型被指定為i2c_bus_type */ 24 struct device_driver driver; 25 /* 存放該驅動支持的設備列表。i2c_device id結構在include/linux/mod_devicetable.h 26 中定義。包括一個char name[I2C_NAME_SIZE]和一個kernel_ulong_t driver_data的成員。 27 name用於匹配設備和驅動,i2c_bus_type的match()方法會遍歷驅動id_table中的每一項,通過 28 比較設備名稱和這個name成員,找到與設備匹配的驅動。*/ 29 const struct i2c_device_id *id_table; 30 31 int (*detect)(struct i2c_client *, struct i2c_board_info *); 32 const unsigned short *address_list; 33 struct list_head clients; 34 };
(5)i2c_client結構
i2c_client結構定義在inlcude/linux/i2c.h中。i2c_client對應於真實的物理設備,每個I2C設備都需要一個i2c_client描述。i2c_client的信息通常在BSP的板級文件中通過i2c_board_info填充或者設備樹填充。
1 struct i2c_client { 2 unsigned short flags; // 兩個主要的標識是:I2C_CLIENT_TEN表示設備使用10-bit地址; 3 // I2C_CLIENT_PEC表示設備使用SMBus包錯誤檢查 4 unsigned short addr; // 設備地址,7-bit地址格式下,地址存放該成員的低7位 5 char name[I2C_NAME_SIZE]; // 設備名 6 struct i2c_adapter *adapter; // 依附的適配器 7 struct i2c_driver *driver; // 設備綁定的驅動 8 struct device dev; // 內嵌device結構。 9 int irq; 10 struct list_head detected; 11 };
(1)BSP板級文件的i2c_board_info填充:
I2C設備ID為“ad7142_joystick”、地址為0x2C、中斷號為IRQ_PF5。
1 static struct i2c_board_info __initdata xxx_i2c_board_info[] = { 2 { 3 I2C_BOARD_INFO("ad7142_joystick", 0x2C); 4 .irq = IRQ_PF5. 5 }, 6 ... 7 }
(2)設備樹填充:
I2C設備ID為“invensense,mpu6050”、地址為0x68、中斷號為3。
i2c@138B0000 { samsung,i2c-sda-delay = <100>; samsung,i2c-max-bus-freq = <20000>; pinctrl-0 = <&i2c5_bus>; pinctrl-names = "default"; status = "okay"; mpu6050-3-axis@68 { compatible = "invensense,mpu6050"; reg = <0x68>; interrupt-parent = <&gpx3>; interrupts = <3 2>; }; };
在I2C匯流排驅動i2c_bus_type的match()函數i2c_device_match()中,會調用i2c_match_id()函數匹配板級文件中定義的ID和i2c_driver所支持的ID表。
3 I2C子系統介面
(1)i2c_add_adapter()
i2c_add_adapter()向系統註冊一個i2c_adapter對象,不必指定其nr成員,匯流排編號自動在i2c_add_adapter中分配並賦值給nr。
函數原形 |
int i2c_add_adapter(struct i2c_adapter *adapter); |
函數參數 |
adaper:i2c_adapter結構指針 |
(2)i2c_del_adaper()
i2c_del_adapter()向系統註銷一個i2c_adapter對象。
函數原形 |
int i2c_del_adapter(struct i2c_adapter *adapter); |
函數參數 |
adaper:i2c_adapter結構指針 |
(3)i2c_add_numbered_adapter()
i2c_add_numbered_adapter()向系統註冊一個i2c_adapter結構。此函數必須靜態指定一個匯流排編號給nr成員。
函數原形 |
int i2c_add_numbered_adapter(struct i2c_adapter *adapter); |
函數參數 |
adaper:i2c_adapter結構指針 |
(4)i2c_add_driver()
i2c_add_driver()向系統添加一個i2c_driver對象。
函數原形 |
int i2c_add_driver(struct i2c_driver *driver); |
函數參數 |
driver:i2c_driver結構指針 |
(5)i2c_del_driver()
i2c_del_driver()向系統註銷一個i2c_driver對象。
函數原形 |
int i2c_del_driver(struct i2c_driver *driver); |
函數參數 |
driver:i2c_driver結構指針 |
(6)i2c_master_send()
i2c_master_send()函數用來主機向i2c_client設備對象發送數據。在使用它們收發數據時,必須先設置設備地址。
函數原形 |
int i2c_master_send(const struct i2c_client *client, const char *buf, int count); |
函數參數 |
client:i2c_client結構指針 |
buf:存放發送數據的緩衝區 |
|
count:發送數據的大小 |
(7)i2c_master_recv()
i2c_master_recv()函數用來主機獲取從機的數據。在它們收發數據時,必須先設置設備節點。
函數原形 |
int i2c_master_recv(const struct i2c_client *client, const char *buf, int count); |
函數參數 |
client:i2c_client結構指針 |
buf:存放接收數據的緩衝區 |
|
count:接收數據的大小 |
(8)i2c_transfer()
i2c_transfer()函數用於執行單個或組合的MSG消息。
函數原形 |
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); |
函數參數 |
adapter:I2C適配器的結構指針 |
msgs:i2c_msg結構,存放發送和接收的數據 |
|
num:msg的數量 |
【溫馨提示】i2c_transfer()一次可以傳輸多個i2c_msg(考慮到很多外設的讀寫波形比較複雜)。而對於時序比較簡單的外設,i2c_master_send()函數和i2c_master_recv()函數內部調用i2c_transfer()函數分別完成一條寫消息和一條讀消息。