linux内核的I2C子系统

1、I2C总线汇总概念

  (1)三根信号线:SCL、SDA、GND

  (2)同步、串行、电平、低速、近距离

  (3)总线式结构,支持多个设备挂接在同一条总线上

  (4)主从式结构,通信双方必须一个为主(master)一个为从(slave),主设备掌握每次通信的主动权,从设备按照主设备的节奏被动响应(如果从设备需要主设备访问,需要增加中断功能,比如触摸屏,当有位移变化,触摸屏芯片会产生一个中断,然后主设备就会访问从设备)。每个从设备在总线中有唯一的地址(slave address),主设备通过从地址找到自己要通信的从设备(本质是广播)。

  (5)I2C主要用途就是主SoC与外围设备之间的通信,最大的优势就是总线上可以扩展多个外围设备的支持,比如常见的物联网传感器芯片。

  (6)电容触摸屏芯片的多个引脚构成2个接口。一个接口是I2C的,负责和主SoC连接(本身作为从设备),主SoC通过该接口初始化及控制电容触摸屏芯片、芯片通过该接口向SoC汇报触摸事件的信息(触摸坐标等),我们使用电容触摸屏时重点关注的是这个接口;另一个接口是电容触摸板的管理接口,电容触摸屏芯片通过该接口来控制触摸板硬件。该接口是电容触摸屏公司关心的,他们的触摸屏芯片内部固件编程要处理这部分,我们使用电容触摸屏的人并不关心这里。

 

2、linux内核的I2C驱动框架总览

  (1)I2C驱动框架的主要目标是:让驱动开发者可以在内核中方便的添加自己的I2C设备的驱动程序,从而可以更加容易的在linux下驱动自己的I2C接口硬件。

  (2)源码中I2C相关的驱动均位于:driver/i2c目录下。linux系统提供两种I2C驱动实现方法:第一种叫i2c-dev,对应于driver/i2c/i2c-dev.c,这种方法只是封装了主机(I2C master,一般是SOC中内置的I2C控制器)的I2C的基本操作,并且向应用层提供相应的操作接口,应用层代码需要自己去实现对slave的控制和操作,所以这种I2C驱动相当于只是提供给应用层可以访问slave硬件设备的接口,本身并未对硬件做任何操作,应用需要实现对硬件的操作,因此写应用的人必须对硬件非常了解,其实相当于传统的驱动中干的活儿丢给应用去做了,所以这种I2C驱动又叫做“应用层驱动”,这种方式并不主流,它的优势是把差异化都放在应用中,这样在设备比较难缠(尤其是slave是非标准I2C时)时不用动驱动,而只需要修改应用就可以实现对各种设备的驱动。

  (3)第二种I2C驱动是所有的代码都放在驱动层实现,直接向应用层提供最终结果。应用层甚至不需要知道这里面有I2C存在,譬如电容式触摸屏,直接向应用层提供dev/input/event1的操作接口,应用层编程的人根本不知道event1中涉及到了I2C。

 

3、I2C子系统的4个关键结构体

  (1)struct i2c_adapter,I2C适配器(即I2C主机控制器驱动),主要是用于操控S5PV210内部的I2C外设,S5PV210内部的I2C外设包含5个寄存器(I2CCON0、I2CSTAT0、I2CADD0、I2CDS0、I2CLC0),该文件就是用来操控这5个寄存器,与I2C从设备没有任何关系。

  (2)struct i2c_algorithm,I2C算法,主要是用来描述I2C主控设备与从机之间进行通信的算法(即时序),I2C主控与外设通信是通过I2C适配器(主控制器)的,所以该结构体一般是包含在i2c_adapter里面

  (3)struct i2c_client,I2C(从机)设备信息,硬件的信息放在i2c_client文件里面,i2c_client和i2c_driver在I2C总线里面进行匹配,匹配上之后i2c_client将硬件信息传给i2c_driver,i2c_driver在probe函数里面使用这些硬件信息对硬件进行初始化和操作,从而使硬件可以工作。

  (4)struct i2c_driver, I2C(从机)设备驱动

 

4、关键文件分析

  (1)i2c-core.c 驱动开发中内核给我们实现的那一部分,里面的代码全部和硬件无关,主要是I2C软件层面的一些代码。

  (2)busses目录 里面放置的是一些已经移植好的i2c_adapter(主控制器驱动),我们主要分析的是i2c_s3c2410.c(这里面放置的就是S3C2410的i2c主控制器的操作(S5PV210内置的I2C主控制器与S3C2410的一样(5个寄存器一样))),

  (3)algos目录 linux内核自己实现的一些算法。

 

5、I2C子系统代码之i2c-core.c详细分析

  (1)该文件也是当作一个模块来实现的,因此需要找到insmod和rmmod所执行的函数。
      postcore_initcall(i2c_init); //说明该函数不是动态加载的,而是开机的时候加载的模块。i2c_init主要是用来装载I2C总线(整个I2C是怎样初始化起来的)
       retval = bus_register(&i2c_bus_type); //注册了一个I2C总线模块

    module_exit(i2c_exit); 

  (2)i2c总线的匹配机制

    a、match函数
    b、probe函数

    
    总结:I2C总线上有2条分支:i2c_client链和i2c_driver链,当任何一个driver或者client去注册时,I2C总线都会调用match函数去对client.name和driver.id_table.name进行循环匹配。如果driver.id_table中所有的id都匹配不上则说明client并没有找到一个对应的driver,没了;如果匹配上了则标明client和driver是适用的,那么I2C总线会调用自身的probe函数,自身的probe函数又会调用driver中提供的probe函数,driver中的probe函数会对设备进行硬件初始化和后续工作。

  (3)核心层开放给其他部分的注册接口
    a、i2c_add_adapter/i2c_add_numbered_adapter   注册adapter(主机控制器端的注册)的,比如在i2c-s3c2410.c里面调用 i2c_add_numbered_adapter(&i2c->adap)注册一个adapter;

    b、i2c_add_driver   注册driver的
    c、i2c_new_device   注册client的

 

6、I2C子系统代码之i2c-adapter.c详细分析

   (1)该文件主要是用来存放s3c2410的i2c主机控制器的操作函数。

  (2)i2c主机控制器(adapter)是s5pv210的一个内置外设,所以这里调用platform_driver_register()函数注册了一个平台总线驱动,其对应的设备信息在mach-x210.c的smdkc110_devices中,匹配是通过其中的name进行的。

  (3)s3c24xx_i2c_probe函数分析

    a、填充一个i2c_adapter结构体,并且调用接口去注册之

     b、从platform_device接收硬件信息,做必要的处理(request_mem_region & ioremap、request_irq等)

    c、对硬件做初始化(直接操作210内部I2C控制器的寄存器

 

7、I2C子系统代码之i2c_algorithm详细分析

   (1)i2c->adap.algo = &s3c24xx_i2c_algorithm;

   (2)functionality
   (3)s3c24xx_i2c_doxfer

 

 

8、I2C子系统代码之i2c_driver与i2c_client分析

 (1)i2c_client直接来源:i2c_register_board_info

  smdkc110_machine_init
    i2c_register_board_info

  struct i2c_board_info {
    char type[I2C_NAME_SIZE]; // 设备名
    unsigned short flags; // 属性
    unsigned short addr; // 设备从地址
    void *platform_data; // 设备私有数据
    struct dev_archdata *archdata;
    #ifdef CONFIG_OF
      struct device_node *of_node;
    #endif
    int irq; // 设备使用的IRQ号,对应CPU的EINT
  };

 

 

 (2)实现原理分析

  内核维护一个链表 __i2c_board_list,这个链表上链接的是I2C总线上挂接的所有硬件设备的信息结构体。也就是说这个链表维护的是一个struct i2c_board_info结构体链表。
真正的需要的struct i2c_client在别的地方由__i2c_board_list链表中的各个节点内容来另外构建生成。

  函数调用层次:
  i2c_add_adapter/i2c_add_numbered_adapter
    i2c_register_adapter
      i2c_scan_static_board_info
        i2c_new_device
          device_register

总结:I2C总线的i2c_client的提供是内核通过i2c_add_adapter/i2c_add_numbered_adapter接口调用时自动生成的,生成的原料是mach-x210.c中的i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));

 

补充:gslX680驱动的移植实践
  (1)源码获取
  (2)源码加入内核中
  (3)mach文件中添加board_info
  (4)编译后内核去启动
5.9.13.2、在内核配置中添加CONFIG项
  (1)定义一个宏名,譬如CONFIG_TOUCHSCREEN_GSLX680
  (2)在代码中使用宏来条件编译
  (3)在Makefile中使用宏来条件配置
  (4)在Kconfig项目中添加宏的配置项
  (5)make menuconfig并选择Y或者N

 

posted @ 2018-12-10 22:31  ZQ_One  阅读(1076)  评论(0编辑  收藏  举报