立即注册
查看: 2098|回复: 1

[linux技术资料] 关于LINUX I2C驱动分析(一)

发表于 2015-11-17 15:26:44 | 显示全部楼层 |阅读模式 来自 广东省深圳市罗湖区
12.png
24C08的I2C接口是与2440的IICSCL/IICSDA直接相连的。在2440内部集成了一个I2C控制器,可以通过寄存器来控制它。先来和这四个寄存器混个脸熟吧,后面分析时还会经常用到这四个寄存器。
13.png
在mini2440的板文件中可以找到关于at24c08的内容,如下:
  1. /*
  2. * I2C devices
  3. */  
  4. static struct at24_platform_data at24c08 = {  
  5.     .byte_len   = SZ_8K / 8,  
  6.     .page_size  = 16,  
  7. };  
  8.   
  9. static struct i2c_board_info mini2440_i2c_devs[] __initdata = {  
  10.     {  
  11.         I2C_BOARD_INFO("24c08", 0x50),  
  12.         .platform_data = &at24c08,  
  13.     },  
  14. };  
  15.   
  16. static void __init mini2440_init(void)  
  17. {  
  18.    ... ...  
  19.    i2c_register_board_info(0, mini2440_i2c_devs,  
  20.                 ARRAY_SIZE(mini2440_i2c_devs));  
  21.    ... ...  
  22. }  
复制代码
可以看出,在mini2440的init函数中注册了一个i2c的设备,这个设备我们使用了一个结构体i2c_board_info来描述。这个结构体定义在i2c.h文件中。如下:
  1. struct i2c_board_info {  
  2.     char        type[I2C_NAME_SIZE];  
  3.     unsigned short  flags;  
  4.     unsigned short  addr;  
  5.     void        *platform_data;  
  6.     struct dev_archdata *archdata;  
  7.     int     irq;  
  8. };  
复制代码
其中的platform_data又指向一个at24_platform_data结构体。


以上只是at24c08的部分,在板文件中还可以看到关于2440内部i2c控制器的部分,如下:
  1. static struct platform_device *mini2440_devices[] __initdata = {  
  2. ... ...  
  3. &s3c_device_i2c0,  
  4. ... ...  
  5. };  
  6.   
  7. static void __init mini2440_init(void)  
  8. {  
  9. ... ...  
  10. platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));  
  11. ... ...  
  12. }  
复制代码

其中s2c_device_i2c0定义在arch/arm/plat-s3c/Dev-i2c0.c中(在同一目录下还可以看到很多Dev-开头的c文件,都是2440内部集成的各种设备),仔细看下面的代码再对比2440的datasheet就可以很清楚的知道:

   * 控制器的IO起始地址为S3C_PA_IIC =0x54000000,大小是4K,中断号是43 = IRQ_IIC      S3C2410_IRQ(27)

   * 控制器名是"s3c2410-i2c"

  1. static struct resource s3c_i2c_resource[] = {  
  2.     [0] = {  
  3.         .start = S3C_PA_IIC,  
  4.         .end   = S3C_PA_IIC + SZ_4K - 1,  
  5.         .flags = IORESOURCE_MEM,  
  6.     },  
  7.     [1] = {  
  8.         .start = IRQ_IIC,  
  9.         .end   = IRQ_IIC,  
  10.         .flags = IORESOURCE_IRQ,  
  11.     },  
  12. };  
  13.   
  14. struct platform_device s3c_device_i2c0 = {  
  15.     .name         = "s3c2410-i2c",  
  16. #ifdef CONFIG_S3C_DEV_I2C1  
  17.     .id       = 0,  
  18. #else  
  19.     .id       = -1,  
  20. #endif  
  21.     .num_resources    = ARRAY_SIZE(s3c_i2c_resource),  
  22.     .resource     = s3c_i2c_resource,  
  23. };  
复制代码

2. Linux中I2C驱动框架分析

    这部分是本文的重点部分。根据上面的电气连接关系我们可以看出,我们要想操作24c08,必须要做两方面的驱动。

       第一方面: 2440中I2C控制器的驱动,有了这部分驱动,我们才可以操作控制器来产生I2C的时序信号,来发送数据和接收数据。

       第二方面: 24C08的驱动,有了这部分驱动,才能使用控制器正确操作芯片,来读取和存放数据。

    在Linux系统中,对上边第一方面的实现叫做I2C总线驱动,对第二方面的实现叫做I2C设备驱动。一般来说,如果CPU中集成了I2C控制器并且Linux内核支持这个CPU,那么总线驱动方面就不用我们操心了,内核已经做好了。但如果CPU中没有I2C控制器,而是外接的话,那么就要我们自己实现总线驱动了。对于设备驱动来说,一般常用的驱动也都包含在内核中了,如果我们用了一个内核中没有的芯片,那么就要自己来写了。

    Linux中I2C体系结构如下图所示(图片来源于网络)。图中用分割线分成了三个层次:用户空间(也就是应用程序),内核(也就是驱动部分)和硬件(也就是实际物理设备,这里就是2440中的i2c控制器和at24c08)。这个够清晰了吧?我们现在就是要研究中间那一层。

14.png

由上图我们还可以看出哪些信息呢?

  1). 可以看到几个重要的组成部分,它们是:Driver,Client,i2c-dev,i2c-core,Algorithm,Adapter。这几个部分在内核中都有相应的数据结构,定义在i2c.h文件中,尽量避免粘贴打断代码来凑数,就不贴出来了。简要概括一下每个结构体的意义。

       Driver --> struct i2c_driver

          这个结构体对应了驱动方法,重要成员函数有probe,reMOVe,suspend,resume。

          还包括一个重要的数据结构: struct i2c_device_id *id_table; 如果驱动可以支持好几个设备,那么这里面就要包含这些设备的ID


       Client --> struct i2c_client

          应用程序是选择性失明的,它只能看到抽象的设备文件,其他部分都是看不见的。图中只有Client与应用程序有联系,所以我们可以大胆得出结论:这个Client是对应于真实的物理设备,在本文就是at24c08。 所以很显然这个结构体中的内容应该是描述设备的。包含了芯片地址,设备名称,设备使用的中断号,设备所依附的控制器,设备所依附的驱动等内容。


       Algorithm -->struct i2c_algorithm

          Algorithm就是算法的意思。在这个结构体中定义了一套控制器使用的通信方法。其中关键函数是master_xfer()。我们实际工作中的重要一点就是要实现这个函数。

  1. int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
复制代码

Adapter --> struct i2c_adapter

           这个结构体对应一个控制器。其中包含了控制器名称,algorithm数据,控制器设备等。


2). 可以看出,i2c-core起到了关键的承上启下的作用。事实上也是这样,我们将从这里展开来分析。源代码位于drivers/i2c/i2c-core.c中。在这个文件中可以看到几个重要的函数。

   *增加/删除i2c控制器的函数

  1. int i2c_add_adapter(struct i2c_adapter *adapter)  
  2. int i2c_del_adapter(struct i2c_adapter *adap)
复制代码

*增加/删除设备驱动的函数

  1. int i2c_register_driver(struct module *owner, struct i2c_driver *driver)  
  2. void i2c_del_driver(struct i2c_driver *driver)  
复制代码

*增加/删除i2c设备的函数

  1. struct i2c_client *  
  2. i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info);  
  3. void i2c_unregister_device(struct i2c_client *client)  
复制代码

注:在2.6.30版本之前使用的是i2c_attach_client()和i2c_detach_client()函数。之后attach被merge到了i2c_new_device中,而detach直接被unresister取代。实际上这两个函数内部都是调用了device_register()和device_unregister()。源码如下:

    *I2C传输、发送和接收函数

  1. int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);  
  2.   
  3. int i2c_master_send(struct i2c_client *client,const char *buf ,int count);  
  4.   
  5. int i2c_master_recv(struct i2c_client *client, char *buf ,int count);  
复制代码

其中send和receive分别都调用了transfer函数,而transfer也不是直接和硬件交互,而是调用algorithm中的master_xfer()函数,所以我们要想进行数据传输,必须自己来实现这个master_xfer()函数,这是总线驱动开发的重点之一。下面以read()系统调用的流程

[/hide]来简单梳理一下:

15.png



发表于 2015-11-18 11:32:43 | 显示全部楼层 来自 广东省深圳市罗湖区
linux的资料能够发点给我不
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

合作/建议

TEL: 19168984579

工作时间:
周一到周五 9:00-11:30 13:30-19:30
  • 扫一扫关注公众号
  • 扫一扫打开小程序
Copyright © 2013-2024 一牛网 版权所有 All Rights Reserved. 帮助中心|隐私声明|联系我们|手机版|粤ICP备13053961号|营业执照|EDI证
在本版发帖搜索
扫一扫添加微信客服
QQ客服返回顶部
快速回复 返回顶部 返回列表