linux中I2C驱动框架

一、总体介绍
四个结构体:
控制器部分:
struct i2c_adapter    i2c控制器(对应SOC部分)
struct i2c_agorithm   i2c算法(对应i2c怎样穿数据,包括在i2c_adapter中)
触摸屏芯片部分:
struct i2c_client     i2c设备(具有i2c接口的设备)
struct i2c_driver     i2c设备的驱动(与i2c_client配对)


几个部分的文件,每个部分的文件可以看成是一个模块,都可以以平台总线
的方式来实现,非常灵活,一般需要获取硬件资源的模块会设计成平台总线
式的结构,这样可以程序与数据分离,便于移植。是很好的一种设计理念。

接口文件i2c-core.c:
1、定义了i2c总线
i2c_bus_type
    mach
    probe
    
2、提供了一些驱动开发的接口
i2c_add_adapter/i2c_add_numbered_adapter   注册adapter(控制器部分)
i2c_register_driver                        注册driver
i2c_new_device                             注册client


二、控制器adapter部分(在文件i2c-s3c2410.c):

其采用的平台总线的方式实现,platform_driver部分是对SOC的I2C控制器
部分进行操作的程序;platform_device是SOC的I2C控制器部分的硬件资源
采取数据与程序分离的思想。
platform_driver:
platform_driver_register(&s3c24xx_i2c_driver)将驱动注册到platform_bus_type总线上

platform_device:
1、定义一个platform_device结构体:&s3c_device_i2c0
2、在machine_init函数中platform_add_devices将设备注册到platform_bus_type总线上
3、在machine_init函数中使用函数s3c_i2c0_set_platdata添加设备的私有数据

mach:
当platform_driver与platform_device采用platform_bus_type的mach函数
匹配上了之后,就会先调用platform_bus_type的probe函数,如果该函数
不存在,就会调用platform_driver的probe函数。

probe:
在驱动的probe函数中执行的流程如下:
s3c24xx_i2c_probe
    // 进行一些列的分配设置以及I2C控制器方面的初始化操作
    ..............
    // 注册adapter(该函数是i2c-core.c中提供的接口函数)
    ret = i2c_add_numbered_adapter(&i2c->adap)


三、i2c设备芯片部分(以gslX680触摸屏为例):
其采用的是i2c总线的实现方式,i2c_driver对应的是i2c设备芯片部分的
操作程序;i2c_client对应i2c设备芯片部分的硬件资源。采取数据与代码
分离的思想。
i2c_driver:
ret = i2c_add_driver(&gsl_ts_driver)将i2c_driver注册到i2c_bus_type总线上

i2c_client:
i2c设备的构建比较有意思,驱动框架中留给驱动开发人员写i2c设备驱动两种
方式。一种是静态方式:事先构建好i2c_board_info结构体,采用此结构体
信息构建i2c_client结构体并将其注册到i2c总线上;第二种采用动态的方式:
i2c_driver会根据自己的address_list信息去detect相应地址处的i2c设备,
如果确认有该设备存在,则构建相应的i2c_client结构体,并将其注册到i2c总线上。

方式一:
i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0))
        list_add_tail(&devinfo->list, &__i2c_board_list)
挂接硬件信息的__i2c_board_list链表很重要,其他地方根据这个i2c_devinfo
结构体构造i2c_client

i2c_add_adapter/i2c_add_numbered_adapter
    i2c_register_adapter
        i2c_scan_static_board_info
            list_for_each_entry(devinfo, &__i2c_board_list, list) {
                if (devinfo->busnum == adapter->nr
                    && !i2c_new_device(adapter,
                            &devinfo->board_info))
                }            
根据i2c_board_info构建i2c_client并将其注册到i2c总线上。

方式二:
i2c_add_adapter/i2c_add_numbered_adapter
    i2c_register_adapter
        dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter)
            i2c_do_add_adapter(to_i2c_driver(d), data)
                i2c_detect(adap, driver)
                    if (!driver->detect || !address_list)
                        return 0;
                    i2c_detect_address(temp_client, driver)
                        err = driver->detect(temp_client, &info)
                        client = i2c_new_device(adapter, &info)
根据address_list信息进行detect,如果detect成功,构建相应的i2c_client
并将其注册到i2c总线上。

总结:上面两种方式有两个共同点,一是:注册i2c_client的入口来源都是
i2c_add_adapter/i2c_add_numbered_adapter这两个函数中的其中一个,而
这两个函数是注册adapter控制器的,也就是说i2c_client的注册是在注册
adapter的过程中完成的;二是:注册i2c_client最终都是使用i2c_new_device
函数完成,这是i2c-core.c中留给驱动开发人员的接口。由上面的两种方式
的过程可以得出两种方式实现i2c_client的关键如下:
方式一:
主要是构建好i2c_board_info结构体,该结构体中有两个关键的属性,
type表示i2c_client的name,addr表示i2c_client的设备地址。
方式二:
主要是构建好i2c_driver中的address_list列表,以及实现好i2c_driver
中的detect函数,在该函数中要对i2c_board_info结构体取设备名称。

.mach:
当i2c_driver的id_table中的name与i2c_client中的名字匹配上后,就会调用
i2c_bus_type中的probe函数,最终还是会调用i2c_driver中的probe函数;如果
该函数不存在,则直接调用i2c_driver中的probe函数。

.probe:
在该函数中将触摸屏的驱动实现为一个input子系统。所以该函数中要做的
工作大体上就能总结为一下几点(前面博客input子系统中有讲过):
1、分配input_dev结构体
2、设置该结构体
3、注册input_dev
4、进行硬件相关的操作(gpio、SFR、中断等)

猜你喜欢

转载自blog.csdn.net/Wenlong_L/article/details/82117627
今日推荐