现在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;
评论
查看更多