TQ2440 学习笔记—— 24、IIC 接口

2019-07-13 04:22发布

(韦东山——嵌入式Linux 应用开发完全手册) 
IIC 接口
IIC (Inter-Integrated Circuit)总线是一种由PHILIPS 公司开发的串行总线,用于连接微控制器及其外围设备,它具有如下特点: 1、只有两条总线线路:一条串行数据线(SDA),一条串行时钟线(SCL)。 2、每个连接到总线的器件都可以使用软件根据它的唯一的地址来识别。 3、传输数据的设备间是简单的主从关系。 4、主机可以用主机发送器或主机接收器。 5、它是一个真正的多主机总线,两个或多个主机同时发起数据传输时,可以通过冲突检验和仲裁来防止数据被破坏。 6、串行的 8 位双向数据传输,位速率在标准模式下可达100 kbit/s,在快速模式下可达 400 kbit/s,在高速模式下可达3.4Mbit/s。 7、片上的滤波器可以增强抗干扰功能,保证数据的完整。 8、连接到同一总线上的 IC 数量只受总线的最大电容400 pF 的限制。
IIC 总线术语的定义


I2C在Linux中是Bus下的一个子系统. 它由客户驱动(client driver),i2c-core核心,i2c适配器驱动(adapter driver) ,算法aglorithm组成。s3c2440中有两个i2c现适配器.作为platform_device设备在系统启动先时被注册和添加。下面我们分析i2c(设备,驱动,总线)的实现过程. //填充设备资源
//struct resource结构体描述了挂接在cpu总线上的设备实体资源
//.start:i2c寄存器起始地址; .end:i2c寄存器结束地址; .flag:描述设备实体的共性和特性标志
[cpp] view plaincopy
  1. static struct resource s3c_i2c_resource[] = {  
  2. [0] = {//i2c-0  
  3. .start = S3C_PA_IIC,  
  4. .end   = S3C_PA_IIC + SZ_4K - 1,  
  5. .flags = IORESOURCE_MEM,  
  6. },  
  7. [1] = {//i2c-1   
  8. .start = IRQ_IIC,  
  9. .end   = IRQ_IIC,  
  10. .flags = IORESOURCE_IRQ,  
  11. },  
  12. };  
/i2c适配器初始化时数据
[cpp] view plaincopy
  1. static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {  
  2. .flags = 0,  
  3. .slave_addr = 0x10,  
  4. .frequency = 100*1000,  
  5. .sda_delay = 100,   
  6. };  
//声明i2c适配器为platform_device [cpp] view plaincopy
  1. struct platform_device s3c_device_i2c0 = {  
  2.     .name         = "s3c2410-i2c",  
  3. #ifdef CONFIG_S3C_DEV_I2C1  
  4.     .id       = 0,  
  5. #else  
  6.     .id       = -1,  
  7. #endif  
  8.     .num_resources    = ARRAY_SIZE(s3c_i2c_resource),  
  9.     .resource     = s3c_i2c_resource,  
  10. };  
  11.   
  12. static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {  
  13.     .flags      = 0,  
  14.     .slave_addr = 0x10,  
  15.     .frequency  = 100*1000,  
  16.     .sda_delay  = 100,   
  17. };  
//添加i2c适配器:
[cpp] view plaincopy
  1. static struct platform_device *smdk2440_devices[] __initdata = {  
  2. ...  
  3. &s3c_device_i2c0,  
  4. ...  
  5. };  
//添加plat_from_data
[cpp] view plaincopy
  1. void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd)  
  2. {  
  3. struct s3c2410_platform_i2c *npd;  
  4.   
  5.   
  6. if (!pd)  
  7. pd = &default_i2c_data0;    
  8. npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL);  
  9. if (!npd)  
  10. printk(KERN_ERR "%s: no memory for platform data ", __func__);  
  11. else if (!npd->cfg_gpio)  
  12. npd->cfg_gpio = s3c_i2c0_cfg_gpio;  //i2c引脚配置  
  13.   
  14.   
  15. s3c_device_i2c0.dev.platform_data = npd;  //挂接plat_form_data数据  
  16. }  
//定义好上面相关结构后,在smdk2440_machine_init()中被注册和添加成platform_device
[cpp] view plaincopy
  1. static void __init smdk2440_machine_init(void)  
  2. {  
  3. s3c24xx_fb_set_platdata(&smdk2440_fb_info);  
  4. s3c_i2c0_set_platdata(NULL);  
  5.        ...  
  6. //注册和添加platform_device  
  7. platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));  
  8. ...  
  9. }  
其中 smdk2440_machine_init在被赋值在MACHINE_START中
在start_kernel()-->setup_arch()时被调用,但值得注意的是i2c适配器并没有被初始化,因为还没有驱动!
通过下面可以知道platform_device_register()和device_register()的区别:
[cpp] view plaincopy
  1. platform_add_devices()-->platform_device_register()-->  
  2. platform_device_add()--> device-->add()  
platfrom_bus_init()时也将添加一个名叫platform的设备(struct device platform_bus):
[cpp] view plaincopy
  1. plat_form_bus_init()-->device_register()-->device_register()  
  2. -->device-->add()  
但这个设备是虚拟的,所有platform_device_add()后的设备都是在/devices/platform/下
因为所有plaform_device 的父母亲都是platform_bus,是在platform_device_add()中
[cpp] view plaincopy
  1. if (!pdev->dev.parent)  
  2. pdev->dev.parent = &platform_bus;  
我想这也就是platform_device_register()和device_register()区别吧
在reset_init()-->kernel_init()-->do_basic_setup()
-->driver_init()-->platform_bus_init()完成platform_bus总线的注册
但是现在i2c适配器并没有和驱动绑上,因为到系现在为止驱动还没有出现呢(初始化)
只有做好前面一些的准备功夫,i2c适配器驱动才能初始化,这个是需要按照顺序来的。
s3c2440-i2c适配器驱动的初始化在drivers/i2c/bus/i2c-s3c2410.c中实现
并且作为platform_driver注册。
//填充driver结构并完成相应probe,remove等函数
[cpp] view plaincopy
  1. static struct platform_driver s3c2440_i2c_driver = {  
  2. .probe = s3c24xx_i2c_probe,  
  3. .remove = s3c24xx_i2c_remove,  
  4. .suspend_late = s3c24xx_i2c_suspend_late,  
  5. .resume = s3c24xx_i2c_resume,  
  6. .driver = {  
  7. .owner = THIS_MODULE,  
  8. .name = "s3c2440-i2c"//  
  9. },  
  10. };  
//初始化并注册platform_driver [cpp] view plaincopy
  1. static int __init i2c_adap_s3c_init(void)  
  2. {  
  3.     int ret;  
  4.   
  5.   
  6.     ret = platform_driver_register(&s3c2410_i2c_driver);//  
  7.     if (ret == 0) {  
  8.         printk("register s3c2440_i2c_driver..... ");  
  9.         ret = platform_driver_register(&s3c2440_i2c_driver);  
  10.         if (ret)  
  11.             {  
  12.             printk("register s3c2410_i2c_driver..... ");  
  13.             platform_driver_unregister(&s3c2410_i2c_driver);  
  14.             }  
  15.     }  
  16.   
  17.   
  18.     return ret;  
  19. }  
  20. subsys_initcall(i2c_adap_s3c_init);  
这样适配器就和驱动绑定上了,过程是这样的:
[cpp] view plaincopy
  1. platform_driver_register()-->driver_register()-->bus_add_driver()-->driver_attach()  
  2. __driver_attach()-->driver_probe_device()-->s3c24xx_i2c_probe()  
并且在s3c24xx_i2c_probe()的时候调用
[cpp] view plaincopy
  1. i2c_add_numbered_adapter(&i2c->adap);  
最后添加自己为i2c总线的适配器。这样分析过程也就结束了