現在ARM下對SoC開發板的硬件描述都是采用devicetree文件,使用linux自帶的dtc程序將dts編譯成dtb之后,由u-boot將dtb導入給linux內核,linux內核讀取dtb,然后注冊設備的resource,linux內核使用of_系列函數API讀取硬件資源。具體的說明可以看下: .dts文件根據具體的硬件配置好后,編譯生成.dtb文件。
然后需要在menuconfig內核配置中為硬件選擇驅動程序,只有硬件驅動程序和dts中的硬件名字匹配時,才能觸發驅動的probe函數
rtc-8564和pcf8563的驅動是兼容的,均為pcf8563驅動。
注:以下的分析基于3.12.0linux內核。個人分析難免存在紕漏,懇請大家指正。
一、I2C的linux主要涉及4個結構體:i2c_adapter,i2c_algorithm,i2c_client,i2c_driver
struct i2c_adapter {
struct module *owner;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
/* data fields that are valid for all devices */
struct rt_mutex bus_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
};
i2c總線控制器數據依附于algo_data,比如xi2cps,s3c24xx_i2c。
struct device dev;成員表明i2c_adapter是一個硬件,對應SoC上的I2C控制器。而i2c_algorithm則是這個I2C控制器的底層驅動程序。
同理:
struct i2c_client {
unsigned short flags; /* div., see below */
unsigned short addr; /* chip address - NOTE: 7bit */
/* addresses are stored in the */
/* _LOWER_ 7 bits */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct i2c_driver *driver; /* and our access routines */
struct device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
};
struct i2c_client代表一個掛載到i2c總線上的i2c從設備,該設備所需要的數據結構,其中包括
該i2c從設備所依附的i2c主設備 struct i2c_adapter *adapter
該i2c從設備的驅動程序struct i2c_driver *driver
作為i2c從設備所通用的成員變量,比如addr, name等
該i2c從設備驅動所特有的數據,依附于dev->driver_data下,在i2c_driver中的probe函數中設置這個結構體成員。比如eeprom的eeprom_data。
所有i2c從設備組成的雙向鏈表:detected
struct device dev表明struct i2c_client代表的是一個硬件,比如eeprom芯片,或則rtc芯片,通過i2c總線連接到i2c_adapter硬件上。
而i2c_driver則是這個i2c_client芯片硬件的驅動程序。
我們一般會對每個I2C字符設備定義一個私有信息結構體,而i2c_client一般被包含在這個私有信息結構體中。看過LDR3源代碼的hacker應該比較清楚。
i2c_client依附于i2c_adapter,也就是I2C設備和I2C總線控制器的對應關系,一個i2c_adapter可以掛接多個i2c_client,i2c_adapter的struct list_head userspace_clients;結構成員就是所有client的鏈表。
linux的最新版本基本上支持目前所有的I2C適配器硬件和I2C從設備,但是對于工程師來說,可能要面臨各種情況:為i2c_adapter和i2c_client編寫驅動程序。
二、I2C核心
I2C核心是源碼位于drivers/i2c/i2c-core.c,它并不依賴于硬件平臺的接口函數,是I2C總線驅動和設備驅動的紐帶。
增加/刪除i2c_adapter
int i2c_add_adapter(struct i2c_adapter *adapter)
//調用i2c_register_adapter()
int i2c_del_adapter(struct i2c_adapter *adapter)
增加/刪除i2c_driver
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
int i2c_add_driver(struct i2c_driver *driver)
//調用i2c_register_driver
void i2c_del_driver(struct i2c_driver *driver)
增加/刪除i2c_client
struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
void i2c_unregister_device(struct i2c_client *client)
注:在2.6.30版本之前使用的是i2c_attach_client()和i2c_detach_client()函數。之后attach被merge到了i2c_new_device中,而detach直接被unregister取代。實際上這兩個函數內部都是調用了device_register()和device_unregister()
I2C傳輸、發送接收
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
i2c_transfer()函數用于進行I2C 適配器和I2C 設備之間的一組消息交互,i2c_master_send()函數和i2c_master_recv()函數內部會調用i2c_transfer()函數分別完成一條寫消息和一條讀消息。
i2c_transfer()本身不能和硬件完成消息交互,它尋找i2c_adapter對應的i2c_algorithm,要實現數據傳送就要實現i2c_algorithm的master_xfer(),這個函數與具體的硬件有關,大部分時間由廠商完成。
i2c_transfer()通過調用__i2c_transfer()完成I2C通訊:
int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;
評論
查看更多