linux I2C子系统介绍

一.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;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值