/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (ret = 0, try = 0; try <= adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num);
if (ret != -EAGAIN)
break;
if (time_after(jiffies, orig_jiffies + adap->timeout))
break;
}
return ret;
}
可见retries为重传尝试次数,timeout为超时时间。
三、Linux I2C总线驱动
1、I2C适配器的加载和卸除
加载:申请硬件资源,比如IO地址,中断号,调用i2c_add_adapter加载适配器
i2c_add_adapter中会调用i2c_register_adapter函数
static int i2c_register_adapter(struct i2c_adapter *adap)
{
... ...
device_register(&adap->dev);
//完成I2C主设备adapter的注册,即注册object和发送uevent等
i2c_scan_static_board_info(adap);
//注册i2c_client
... ...
}
static void i2c_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't create device at 0x%02x\n",
devinfo->board_info.addr);
}
up_read(&__i2c_board_lock);
}
i2c_new_device调用device_register注册i2c从设备。
那么,这个I2C从设备组成的双向循环链表,是什么时候通过什么方式建立起来的呢?
以 /arch/arm/mach-pxa/saar.c 为例
static void __init saar_init(void)
{
... ...
saar_init_i2c();
........
}
static void __init saar_init_i2c(void)
{
pxa_set_i2c_info(NULL);
i2c_register_board_info(0, ARRAY_AND_SIZE(saar_i2c_info));
}
static struct i2c_board_info saar_i2c_info[] = {
[0] = {
.type = "da9034",
.addr = 0x34,
.platform_data = &saar_da9034_info,
.irq = PXA_GPIO_TO_IRQ(mfp_to_gpio(MFP_PIN_GPIO83)),
},
};
/* drivers/i2c/i2c-boardinfo.c */
int __init i2c_register_board_info(int busnum, structi2c_board_info const *info, unsigned len)
{
... ...
struct i2c_devinfo *devinfo;
devinfo->board_info = *info;
list_add_tail(&devinfo->list, &__i2c_board_list); //将I2C从设备加入该链表中
... ...
}
所以,在系统初始化的过程中,我们可以通过 i2c_register_board_info,将所需要的I2C从设备加入一个名为__i2c_board_list双向循环链表,系统在成功加载I2C主设备adapt后,就会对这张链表里所有I2C从设备逐一地完成 i2c_client的注册。
也就是说,i2c_client和i2c_adapter都是由i2c_core来维护的。
在xilinx-linux中,i2c从设备是通过dts文件传递给内核的,内核通过zynq_init_machine函数注册所有的i2c从设备,i2c_client.
在linux的设备和驱动管理体系中,所有的非热插拔设备默认是在 init_machine函数成员中加入相应维护设备的双向链表中,包括platform_device和其他的设备。当一个特定的设备驱动通过driver_register加入对应的总线下时,回去遍历对应总线下的设备双向链表,当驱动和设备匹配时,会触发驱动的probe函数。
DT_MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform")
.smp = smp_ops(zynq_smp_ops),
.map_io = zynq_map_io,
.init_irq = zynq_irq_init,
.init_machine = zynq_init_machine,
.init_late = zynq_init_late,
.init_time = zynq_timer_init,
.dt_compat = zynq_dt_match,
.reserve = zynq_memory_init,
.restart = zynq_system_reset,
MACHINE_END
可以参考mach-zynq的电路板初始化代码
卸除:释放硬件资源,调用i2c_del_adapter卸载i2c适配器
void i2c_del_adapter(struct i2c_adapter *adap)
{
..........
list_for_each_entry_safe(client, next, &adap->userspace_clients,
detected) {
dev_dbg(&adap->dev, "Removing %s at 0x%x\n", client->name,
client->addr);
list_del(&client->detected);
i2c_unregister_device(client);
}
卸载所有的从i2c设备
..............
device_unregister(&adap->dev);
卸载i2c适配器
..............
}
电子发烧友App




评论