一.I2C概念:
I2C是philips提出的外设总线.
I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线。
因此,I2C总线被非常广泛地应用在EEPROM,实时钟,小型LCD等设备与CPU的接口中;
。
注意I2c总线初始化调用的是postcore_initcall(i2c_init);,查看其具体的宏我们得知其优先级为2,请留意该优先级,因为与后续的I2c设备驱动注册优先级存在强相关,必须要先有I2c总线,才能将i2c设备挂载到I2c总线上(稍后我们进行具体分析)
而dummy_driver驱动中的dummy_probe,dummy_remove函数为空。系统为什么要这样做呢?
I2c_add_driver主要为适配器添加注册驱动程序
首先,为了支持各种不同的适配器,在此不同将代码固定化,这个core层只是一个适配层, 也就是连接client与适配器的中间层,具体适配器驱动的初始化延迟到内核配置完成之后,也就是确定了某类的适配器之后这个驱动才会初始化。下面以mpc架构来分析适配器驱动的实现。
分析i2c_init,其只做了2件事,其一添加了一个空的driver,其二想内核注册了I2c总线,至此I2c总线注册完成(so easy!)
struct bus_type i2c_bus_type = {
.name = “i2c”,
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
};
static struct i2c_driver dummy_driver = {
.driver.name = “dummy”,
.probe = dummy_probe,
.remove = dummy_remove,
.id_table = dummy_id,
};
文件路径:arch/arm/mach-s3c24xx/mach-mini2440.c
static int __init i2c_init(void)
{
int retval;
retval = of_alias_get_highest_id("i2c");
down_write(&__i2c_board_lock);
if (retval >= __i2c_first_dynamic_bus_num)
__i2c_first_dynamic_bus_num = retval + 1;
up_write(&__i2c_board_lock);
retval = bus_register(&i2c_bus_type);//注册I2C总线到/BUS/目录下
if (retval)
return retval;
is_registered = true;
#ifdef CONFIG_I2C_COMPAT
i2c_adapter_compat_class = class_compat_register(“i2c-adapter”);
if (!i2c_adapter_compat_class) {
retval = -ENOMEM;
goto bus_err;
}
#endif
retval = i2c_add_driver(&dummy_driver);//添加I2C 总线适配器驱动
if (retval)
goto class_err;
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier));
if (IS_ENABLED(CONFIG_ACPI))
WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier));
return 0;
class_err:
#ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
is_registered = false;
bus_unregister(&i2c_bus_type);
return retval;
}
void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd)
{
struct s3c2410_platform_i2c *npd;
if (!pd) {
pd = &default_i2c_data;
pd->bus_num = 0;
}
npd = s3c_set_platdata(pd, sizeof(*npd), &s3c_device_i2c0);
if (!npd->cfg_gpio)
npd->cfg_gpio = s3c_i2c0_cfg_gpio;
}
#ifdef CONFIG_S3C_DEV_I2C1
static struct resource s3c_i2c1_resource[] = {
[0] = DEFINE_RES_MEM(S3C_PA_IIC1, SZ_4K),
[1] = DEFINE_RES_IRQ(IRQ_IIC1),
};
i2c_register_board_info分析
i2c_register_board_info()函数的for循环中,首先会申请I2C设备信息结构体,如果申请成功,将I2C总线号和设备信息赋值给设备信息结构体,并且将设备信息结构体的链表插入到__i2c_board_list中,此处尤为重要,在本文的开头中所提的函数i2c_scan_static_board_info(adap);,此函数就是通过__i2c_board_list链表找到上面注册的设备信息,结合gsc3280_i2c_devices_init()函数和i2c_devices_info结构体,此处for循环的len为3,即正常情况下需要创建三个devinfo结构体,for循环结束后,__i2c_board_list链表中也就有了三个I2C设备的链表项,在程序的其他地方如果需要使用这里注册的设备结构信息,只需要遍历链表__i2c_board_list,通过总线号即可找到相应的设备信息.此一般在板级初始化中调用
adapter以平台总线方式添加,再次将所有挂在在平台总线上的设备逐一加入
int platform_add_devices(struct platform_device **devs, int num)
{
int i, ret = 0;
for (i = 0; i < num; i++) {
ret = platform_device_register(devs[i]);
if (ret) {
while (--i >= 0)
platform_device_unregister(devs[i]);
break;
}
}
return ret;
}