LinuxI2C总线驱动深入分析
LinuxI2C总线驱动深入分析Kernel版本:2.6.32平台: mips 本文目的: 在工作闲暇之余,写点东西,留个念想,也证明一下自己曾经年轻过,主要的内容以I2C总线注册,设备注册,驱动注册为主线,介绍了详细的注册过程及I2C传输数据的过程。欢迎大家来探讨这些技术细节,mall:http://blog.csdn.net/dyron I2C 总线及device
LinuxI2C总线驱动深入分析
Kernel版本:2.6.32
平台: mips
本文目的: 在工作闲暇之余,写点东西,留个念想,也证明一下自己曾经年轻过,主要的内容以I2C总线注册,设备注册,驱动注册为主线,介绍了详细的注册过程及I2C传输数据的过程。欢迎大家来探讨这些技术细节,mall:http://blog.csdn.net/dyron
I2C 总线及device注册过程
首先,I2C注册过程为创建board_info 结构体。
static struct i2c_board_info i2c1_devs[]__initdata = {
{
I2C_BOARD_INFO("lm75",0x48),
},
};
通过i2c_register_board_info, 将i2c_board_info设备结构注册进系统中去,
i2c_register_board_info(1, i2c1_devs, ARRAY_SIZE(i2c1_devs));
i2c_register_board_info 传入的参数,busnum为总线地址,info为总线上设备描述,len为总线描述结构的大小。
int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info,unsigned len)
if (busnum >=__i2c_first_dynamic_bus_num)
__i2c_first_dynamic_bus_num= busnum + 1;
for(status = 0; len; len--, info++) {
structi2c_devinfo *devinfo;
devinfo= kzalloc(sizeof(*devinfo), GFP_KERNEL);
if(!devinfo) {
pr_debug("i2c-core:can't register boardinfo!\n");
status= -ENOMEM;
break;
}
devinfo->busnum= busnum;
devinfo->board_info= *info;
list_add_tail(&devinfo->list,&__i2c_board_list);
}
为每一个I2C设备分配i2c_devinfo结构体, 并注册到__i2c_board_list链表上,这里需要注意的一点是,设备列表上并没有出现按总线区分的现象,而是将所有总线的设备注册到一个统一的列表上。
经过以上这几步,I2C的board_info设备就注册好了,现在分析设备的adapter注册过程,一般设备的总线注册是由SOC厂商提供的,在SOC厂商提供的busses注册时,会遍历设备结点,下面具体分析。
一般在设备注册的probe中,会调用i2c_add_numbered_adapter或者i2c_add_adapter的接口,注册I2C的adapter, 其中就初始化了I2C总线中的设备, 以分析i2c_add_numbered_adapter为例。
i2c_add_numbered_adapter使用静态的总线ID, i2c_add_adapter使用动态的总线ID。
inti2c_add_numbered_adapter(struct i2c_adapter *adap)
{
………
if (status == 0)
status =i2c_register_adapter(adap);
return status;
}
调用i2c_register_adapter来注册传入的adapter.
static inti2c_register_adapter(struct i2c_adapter *adap)
{
………
dev_set_name(&adap->dev,"i2c-%d", adap->nr);
adap->dev.bus = &i2c_bus_type;
adap->dev.type =&i2c_adapter_type;
res = device_register(&adap->dev);
if (res)
goto out_list;
……………..
res =class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
adap->dev.parent);
if (res)
dev_warn(&adap->dev,
"Failed to create compatibility classlink\n");
if (adap->nr <__i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
}
static voidi2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo *devinfo;
down_read(&__i2c_board_lock);
list_for_each_entry(devinfo,&__i2c_board_list, list) {
if (devinfo->busnum ==adapter->nr
&&!i2c_new_device(adapter,
&devinfo->board_info))
dev_err(&adapter->dev,
"Can'tcreate device at 0x%02x\n",
devinfo->board_info.addr);
}
up_read(&__i2c_board_lock);
}
首先,生成bus-device, 设备device的总线ID与adapter类型, 并接着创建class接口, 如果是静态创建的总线设备,就在已注册的I2C设备链表(__i2c_board_list)中进行查找匹配,并通过查找相同的总线ID,找到相匹配的设备ID,生成client端并注册device到系统中去。
以上就是整个I2C总线及总线上的device注册过程。
I2C driver注册过程
I2C驱动注册是通过i2c_add_driver接口进行的, i2c_add_driver调用i2c_register_driver, 传入i2c_driver结构。
int i2c_register_driver(struct module *owner, struct i2c_driver*driver)
{
int res;
/* Can't registeruntil after driver model init */
if(unlikely(WARN_ON(!i2c_bus_type.p)))
return-EAGAIN;
/* add the driver tothe list of i2c drivers in the driver core */
driver->driver.owner= owner;
driver->driver.bus= &i2c_bus_type;
/* When registrationreturns, the driver core
* will have called probe() for allmatching-but-unbound devices.
*/
res =driver_register(&driver->driver);
if (res)
return res;
pr_debug("i2c-core:driver [%s] registered\n", driver->driver.name);
INIT_LIST_HEAD(&driver->clients);
/* Walk the adaptersthat are already present */
mutex_lock(&core_lock);
bus_for_each_dev(&i2c_bus_type,NULL, driver, __attach_adapter);
mutex_unlock(&core_lock);
return 0;
}
由分析i2c_register_driver中得知, driver的注册相对简单, 在函数中填充总线的类型,然后通过driver_register注册进了系统。
通过bus_for_each_dev(&i2c_bus_type,NULL, driver, __attach_adapter);来attach adapter, 相对的,如果先注册驱动程序,也会有attach driver的过程。在这里分析device attach driver的过程,相对attach adapter比较复杂。
接着回到i2c_register_adapter中的bus_for_each_drv中去,查找机应的driver。
intbus_for_each_drv(struct bus_type *bus, struct device_driver *start,
void *data, int (*fn)(struct device_driver*, void *))
{
struct klist_iter i;
struct device_driver *drv;
int error = 0;
if (!bus)
return -EINVAL;
klist_iter_init_node(&bus->p->klist_drivers,&i,
start ? &start->p->knode_bus :NULL);
while ((drv = next_driver(&i))&& !error)
error = fn(drv, data);
klist_iter_exit(&i);
return error;
}
在bus_for_each_drv中,通过回调fn,进行drv和data的匹配, data就是我们的adapter, fn是传入的i2c_do_add_adapter。
static int i2c_do_add_adapter(structdevice_driver *d, void *data)
{
structi2c_driver *driver = to_i2c_driver(d);
structi2c_adapter *adap = data;
/*Detect supported devices on that bus, and instantiate them */
i2c_detect(adap,driver);
/*Let legacy drivers scan this bus for matching devices */
if(driver->attach_adapter) {
/*We ignore the return code; if it fails, too bad */
driver->attach_adapter(adap);
}
return0;
}
在i2c_detect中,首先通过i2c_adapter_id(adapter);获得当前adapter的总线id,
在I2C_DETECT中, structi2c_client *temp_client 首先,检测driver 是否存在address_data数据,如果不存在直接返回。存在的现象欢迎来一起讨论,http://blog.csdn.net/dyron, 主要是一些i2c设备的自动探测功能,目前kernel都采用静态注册在i2c_board_info中了.
address_data= driver->address_data;
if(!driver->detect || !address_data)
return0;
staticint i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
if (!client)
return 0;
driver = to_i2c_driver(drv);
/* match on an id table if there is one*/
if (driver->id_table)
returni2c_match_id(driver->id_table, client) != NULL;
return 0;
}
至此i2c_driver就与i2c_adapter匹配成功。 I2c 的bus match就会调用驱动程序中的probe函数。
I2C的数据传输分析
I2c 的数据传输接口主要有 i2c_smbus_read_byte_data, i2c_smbus_write_byte, i2c_smbus_read_word_data, i2c_master_send, i2c_master_recv等等接口,各传入的参数不同,但最终调用的只有两种接口,一种是i2c_smbus_xfer, 一种是i2c_transfer.
这主要的区分在于smbus与i2c, SMBus与I2C的比较.
SMBus | I2C |
最大传输速度 100kHz | 最大传输速度400kHz |
最小传输速度 10kHz | 无最小传输速度 |
35ms时钟低超时 | 无时钟超时 |
固定的逻辑电平 | 逻辑电平由VDD决定 |
不同的地址类型(保留、动态等) 7位、 | 不同的总线协议(快速命令、 |
暂时更新到这里,后续内容以后再做更新。
更多推荐
所有评论(0)