/* 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為重傳嘗試次數(shù),timeout為超時時間。
三、Linux I2C總線驅(qū)動
1、I2C適配器的加載和卸除
加載:申請硬件資源,比如IO地址,中斷號,調(diào)用i2c_add_adapter加載適配器
i2c_add_adapter中會調(diào)用i2c_register_adapter函數(shù)
static int i2c_register_adapter(struct i2c_adapter *adap)
{
... ...
device_register(&adap->dev);
//完成I2C主設(shè)備adapter的注冊,即注冊object和發(fā)送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調(diào)用device_register注冊i2c從設(shè)備。
那么,這個I2C從設(shè)備組成的雙向循環(huán)鏈表,是什么時候通過什么方式建立起來的呢?
以 /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從設(shè)備加入該鏈表中
... ...
}
所以,在系統(tǒng)初始化的過程中,我們可以通過 i2c_register_board_info,將所需要的I2C從設(shè)備加入一個名為__i2c_board_list雙向循環(huán)鏈表,系統(tǒng)在成功加載I2C主設(shè)備adapt后,就會對這張鏈表里所有I2C從設(shè)備逐一地完成 i2c_client的注冊。
也就是說,i2c_client和i2c_adapter都是由i2c_core來維護(hù)的。
在xilinx-linux中,i2c從設(shè)備是通過dts文件傳遞給內(nèi)核的,內(nèi)核通過zynq_init_machine函數(shù)注冊所有的i2c從設(shè)備,i2c_client.
在linux的設(shè)備和驅(qū)動管理體系中,所有的非熱插拔設(shè)備默認(rèn)是在 init_machine函數(shù)成員中加入相應(yīng)維護(hù)設(shè)備的雙向鏈表中,包括platform_device和其他的設(shè)備。當(dāng)一個特定的設(shè)備驅(qū)動通過driver_register加入對應(yīng)的總線下時,回去遍歷對應(yīng)總線下的設(shè)備雙向鏈表,當(dāng)驅(qū)動和設(shè)備匹配時,會觸發(fā)驅(qū)動的probe函數(shù)。
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的電路板初始化代碼
卸除:釋放硬件資源,調(diào)用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設(shè)備
..............
device_unregister(&adap->dev);
卸載i2c適配器
..............
}
評論
查看更多