全志公司对I2C的讲解

上传人:沈*** 文档编号:87797233 上传时间:2022-05-10 格式:DOC 页数:12 大小:208KB
返回 下载 相关 举报
全志公司对I2C的讲解_第1页
第1页 / 共12页
全志公司对I2C的讲解_第2页
第2页 / 共12页
全志公司对I2C的讲解_第3页
第3页 / 共12页
点击查看更多>>
资源描述
1. 几个基本概念1.1. 设备模型由 总线( bus_type) + 设备( device ) + 驱动( device_driver ) 组成,在该模型下,所有的设 备通过总线连接起来, 即使有些设备没有连接到一根物理总线上, linux 为其设置了一个内部的、 虚拟的 platform 总线,用以维持总线、驱动、设备的关系。因此,对于实现一个 linux 下的设备驱动,可以划分为两大步:1 、设备注册;2 、驱动注册。当然,其中还有一些细节问题:1 、驱动的 probe 函数2 、驱动和设备是怎么进行绑定的。1.2. i2c 设备驱动的几个数据结构i2c_adapter :每一个 i2c_adapter 对应一个物理上的 i2c 控制器,在 i2c 总线驱动 probe 函数中动态创建。通过 i2c_add_adapter 注册到 i2c_core 。i2c_algorithm :i2c_algorithm 中的关键函数 master_xfer() ,以 i2c_msg 为单位产生 i2c 访问需要的信号。不同 的平台所对应的 master_xfer() 是不同的,需要根据所用平台的硬件特性实现自己的 xxx_xfer() 方 法以填充 i2c_algorithm 的 master_xfer 指针;在 A31 上即是 sun6i_i2c_algorithm 函数。i2c_client :代表一个挂载到 i2c 总线上的 i2c 从设备,包含该设备所需要的数据:该 i2c 从设备所依附的 i2c 控制器 struct i2c_adapter *adapter该 i2c 从设备的驱动程序 struct i2c_driver *driver该 i2c 从设备的访问地址 addr, name该 i2c 从设备的名称 name 。2. i2c 总线驱动2.1. 功能划分从硬件功能上可划分为:i2c控制器和i2c外设(从设备)。每个i2c控制器总线上都可以挂载多 个i2c外设。Linux中对i2c控制器和外设分开管理:通过i2c-sun6i.c文件完成了 i2c控制器的设备注册和驱动注册;通过i2c-core.c 为具体的i2c外设提供了统一的设备注册接口和驱动注册接口,它分离了设备驱动device driver和硬件控制的实现细节(如操作 i2c的寄存器)。2.2. i2c-s un 6i.c该文件是与具体硬件平台相关的,对应于A3x系列芯片。该文件实际上是i2c总线驱动的实现,本质上就是向内核注册 i2c总线设备、注册总线驱动、实现总线传输的时序控制算法。i2c控制器被注册为Platform设备,如下:if (twi_used_mask & TWIO_USED_MASK)platform_device_register&s un 6i_twi0_device);if (twi_used_mask & TWI1_USED_MASK)platform_device_register& s un 6i_twi1_device);if (twi_used_mask & TWI2_USED_MASK)platform_device_register& s un 6i_twi2_device);if (twi_used_mask & TWI3_USED_MASK) platform_device_register&s un 6i_twi3_device);if (twi_used_mask)return platform_driver_register(&sun 6i_i2c_driver);需要注意的是:设备与驱动的对应关系是多对一的;即如果设备类型是一样的,会共用同一套驱动,因此上面代码只是注册了一次驱动platform_driver_register(&su n6i_i2c_driver)。设备注册:将i2c控制器设备注册为 platform设备,为每一个控制器定义一个struct platform_device数据结构,并且把.name 都设置为sun6i-i2c(后面会通过名字进行匹配驱动的),然后是调用 platform_device_register()将设备注册至U platform bus 上。设备注册完成后其直观的表现就是在文件系统下岀现:/sys/bus/platform/devices/su n6i-i2c.0通过platform_device_register()进行的注册过程,说到底就是对 struct platform_device这个数据结构的更改,逐步完成.dev.parent、.dev.kobj、.dev.bus的赋值,然后将.dev.kobj加入到platform_bus-kobj 的链表上。驱动注册:步骤和设备注册的步骤类似,也是为驱动定义了一个数据结构:struct platform_driver sun 6i_i2c_driver;因为一个驱动是可以对应多个设备的,而在系统里的3个控制器基本上是一致的(区别就是寄存器的地址不一样),所以上面注册的3个设备共享的是同一套驱动。初始化.probe和.remove函数,然后调用 platform_driver_register进行驱动注册。主要函数调用流 程:platform_driver_register - driver_register - bus_add_driver - driver_attac需要注意的是 driver_attach,这个函数遍历了总线上( platform_bus_type )的所有设备,寻找与 驱动匹配的设备,并把满足条件的设备结构体上的驱动指针指向驱动,从而完成了驱动和设备的匹配(_driver_attach函数完成)。如果匹配到设备,这时就需要执行 platform_bus_type的probe函数,最终会调用设备驱动的probe函数(sun6i_i2c_probe )。2.2.1 sun 6i_i2c_probe在sun6i_i2c_probe函数中完成了大量的工作,包括硬件初始化、中断注册、为每个i2c控制器创建 i2c_adapter 等。1268 pdata = pdev-dev.platform_data;1269 if (pdata = NULL) 1270 return -ENODEV;1271 12721272 res = platform_get_resource(pdev, IORESOURCE_MEM,0);1273 irq = platform_get_irq(pdev,0);1274 if (res = NULL | irq start, resource_size(res), res-ame) return -ENOMEM;首先得到当前设备的私有数据指针,并将其保留在pdata ;进而通过platform_get_resource得到该设备占用的内存资源,并申请:request_mem_region。同时将irq资源也保留下来。strlcpy(i2c-adap .n ame,s un 6i-i2c, sizeof(i2c-adap .n ame); i2c-adap.ow ner = THIS_MODULE; i2c-adap .nr = pdata-b us_num;i2c-adap.retries =3; i2c-adap.timeout = 5* HZ;=I2C_CLASS_HWMON | I2C_CLASS_SPD;=pdata-freque ncy;=irq;=pdata-b us_num;=I2C_XFER_IDLE;i2c-adapclass i2c-bus_freq i2c-irq i2c-bus_ num i2c-statusi2c-suspe nded =0;spin_lock_init(&i2c- lock);ini t_waitqueue_head(&i2c-wait);i2c_adapter,并初始化一个工作队列init_waitqueue_head。初始化* 通过ioremap申请10资源;* 通过request_irq申请irq资源,中断的处理服务函数是:sun6i_i2c_handler ;* sun6i_i2c_hw_init,对i2c控制进行硬件初始化;* i2c-adap.algo = &sun 6i_i2c_algorithm,初始化控制器的总线传输算法,设备驱动调用;* 将初始化好的 i2c_adapter 注册到 i2c_core : i2c_add_numbered_adapter。至此,probe函数完成。222 sun 6i_i2c_core_processi2c控制器的中断服务程序sun6i_i2c_handler调用了 sun6i_i2c_core_process , i2c总线的实际传输控制也是在该函数里完成的。主要流程:1.2.读取i2c控制器当前状态,twi_query_irq_status,保留在 state中;根据state的值进行分支跳转,控制i2c的工作状态;传输完成,调用sun6i_i2c_xfer_complete,唤醒工作队列。3.223 sun 6i_i2c_xfer每一个i2c控制器设备,在驱动绑定后,都会创建一个i2c_adapter,用以描述该控制器,i2c_adapter 的建立与初始化是在驱动probe的时候建立的。每一个i2c_adapter包含了一个i2c_algorithm 结构体的指针,i2c_algorithm是用来对外提供操作i2c控制器的函数接口的,主要是master_xfer函数,对应于i2c-sun6i.c,实际就是:static i nt sun 6i_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, i nt num)该函数的功能是通知i2c_adapter需要对外设进行数据交换,需要交换的信息通过struct i2c_msg *msgs 传入。sun6i_i2c_xfer 实际上是调用了 sun6i_i2c_do_xfer 进行传输。因为i2c总线读写速率有限,sun6i_i2c_do_xfer启动i2c传输后,通过 wait_event_timeout进入休眠,直到中断唤醒或者超时;中断唤醒是由sun 6i_i2c_xfer_complete 完成的3. i2c设备驱动3.1.驱动注册i2c从设备的驱动注册,使用的是i2c-core.c提供的接口:i2c_register_driver ;其调用如下:i2c_register_driver - driver_register - bus_add_driver;对bus_add_driver进行分析:* 关于 device_driver 数据结构的 struct driver_private *p设备驱动模型是通过kobject对设备驱动进行层次管理的,因此device_driver应该包含kobject 成员,linux 是将 kobject 包含在 struct driver_private 中,再在 device_driver 中 包含 struct driver_private ;我们可以理解driver_private 是 device_driver 的私有数据,由内核进行操作。struct driver_private是在驱动注册的开始,动态申请,并初始化的。* klist_i nit(&priv-klist_devices, NULL, NULL);初始化设备链表,每一个与该驱动匹配的device都会添加到该链表下。* priv-kobj.kset = bus-p-drivers_kset;指定该驱动所属的kset ; kobjectnit_an d_add初始化 kobject,并将kobject添加到其对应的kset集合中(即bus-p-drivers_kset )。该函数最终是调用 kobject_addnternal将kobject添加到对应的 kset中;需要主要的是, 如果kobject的pare nt如果为NULL,在此会将其 pare nt设置为所属 kset集合的kobject :pare nt = kobject_get(&kobj-kset-kobj);接下来是为 kobject创建文件夹:create_dir(kobj);从而能从/sys/目录下显示。* driver_attach,将驱动和设备进行绑定将遍历总线上的设备链表,查找可以匹配的设备,并绑定。driver_attach - bus_for_each_dev(drv-bus, NULL, drv, _driver_attach);将函数指针_driver_attach传入bus_for_each_dev,将每个查找得到的device进行驱动匹配。bus_for_each_dev :遍历总线上的所有设备,因为总线上的设备都是bus-p-klist_devices链表上的一个节点,因此该函数其实就是对链表的遍历,具体可以参考klist o_driver_attach(源码位置 drivers/base/dd.c):进行设备和驱动匹配,如果匹配成功,尝试进行绑定。1. 首先进行匹配确认:driver_match_device(drv, dev);调用关系: - drv-bus-match - i2c_device_match- of_driver_match_devicei2c_matchd可以看岀,最终有两种方式进行驱动匹配查询:方法一:通过 of_driver_match_device 对比 of_device_id ;方法二:通过 i2c_match_id 对比 id_table ;方法二实际上就是对比2.如果匹配确认,进行驱动与设备绑定:driver_probe_device ;调用关系: driver_probe_device - really_probe- dev-bus-probedriver_bou nd在really_probe中,首先将设备的驱动指针指向该驱动:dev-driver = drv。probe对应于 i2c_bus_type,dev-bus-probe 即是:i2c_device_probe,最终调用驱动的 函数。最后是driver_bound,将驱动与设备进行绑定:其实就是调用 klist_add_tail :将设备节点添加到驱动的klist_devices;* 调用klist_add_tail,将被注册的驱动添加到总线的klist_drivers上;klist_add_tail(&priv-k no de_bus, &bus-p-klist_drivers);* module_add_driver(drv-ow ner, drv)在 sysfs仓寸建 drivers 目录3.2. 设备注册方式一:i2c设备动态发现注册在 i2c_register_driver 的最后:INIT_LIST_HEAD(&driver- clients);/* Walk the adapters that are already prese nt */ i2c_for_each_dev(driver, _process_ new_driver);观察 i2c_for_each_dev :int i2c_for_each_dev(/oid *data, int (*fn)( struct device *, void *) int res;mutex_lock( & core_lock);res= bus_for_each_dev(&2c_bus_type, NULL, data, fn); mutex_ un lock(& core_lock);return res;其实就是遍历i2c总线上的klist_devices链表,对得到的每一个device,执行processew_driver跟踪 _process_new_driver - i2c_do_add_adapter - i2c_detecti2c_detect 实现了 i2c设备发现:在注册驱动后,通过i2c_detect检测是否有适合的设备连接在总线上。i2c_detect实现如下:* 在每一个 adapter上遍历驱动给岀的地址列表( address_list),由i2c_detect_address函数完成;最终会调用driver-detect (即设备驱动提供的设备发现函数);* 如果发现满足条件的设备,执行i2c_new_device,为设备建立i2c_client ;并且将设备添加至U i2c_bus_type-p-klist_devices 链表上(device_register),通过 bus_add_device 函数完成,最后调用bus_probe_device,尝试绑定驱动。* 将client添加到驱动的设备链表上:list_add_tail(&clie nt-detected, &driver-clie nts)方式二:i2c设备之静态注册Linux 3.3提供了静态定义的方式来注册设备,接口原型:lin ux-3.3/drivers/i2c/i2c-boardi nfo.cint _init i2c_register_boardnfo(nt bus num,struct i2c board info const *info,核心内容:* 申请struct i2c_devinfo,用以描述一个i2c外设; list_add_tail(&devinfo-list, &_i2c_board_list),将 devinfo 加入链表_i2c_board_list,以供后续查找;扫描 _i2c_board_list,创建 clienti2c_register_board_i nfo只是把设备描述符加入到了_i2c_board_list,并没有创建client,当调用 i2c_register_adapter 注册 adapter 时,会扫描 _i2c_board_list,创建 client ;具 体调用:i2c_register_adapter- i2c_sca n_static_boardnfo- i2c_ new_device- device_register在 i2c_new_device 完成了 client 仓U建,以及设备注册device_registerPS :应该在 i2c_register_adapter之前完成,由上面的注册流程可知,i2c_register_board_i nfo 否则_i2c_boardist中的节点不会被扫描到。总结:*由上述分析可知,i2c设备驱动是通过i2c_register_driver 注册的,i2c设备是通过i2c_n ew_device注册的,在最后,这两个函数都尝试进行驱动和设备绑定(driver_attach 和bus_probe_device );因此不管是先注册驱动还是先注册设备,最后都能够将合适的驱动和设备进行绑定*有两种方式进行设备注册:1、 通过i2c_register_board_info,在系统启动之初静态地进行i2c设备注册(axp电源驱动就是这样做的);2、 实现i2c设备驱动的detect函数,在驱动加载的时候动态检测创建设备,aw平台 的触摸屏驱动gt82x.ko就是通过这种方式。 Linux是通过在驱动数据结构中内嵌kobject、kset,完成了设备驱动的层次管理的,理解kobject、kset对理解设备驱动模型很重要。4. i2c驱动架构图1、i2c_add_adapter2、i2c new device/i2c register board info3、i2c_add_driver4、 调用i2c bus中注册的 match函数进行匹配5、调用platform bus中注册的 match函数进行匹配 6、i2cdev_attach_adapter
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 办公文档 > 工作计划


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!