付费下载
下载本文档
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
I2C总线仅仅使用SCL、SDA这两根信号线就实现了设备之间的数 交互,极大地简化了对硬件资源和PCB板布线空间的占用。因此,I2C总线被非常广泛地应用在EEPROM、实时钟、小型LCD等设备与CPU的Linux系统定I2C驱动体系结构Linux系统中,I2C3I2C、I2C总线驱动和I2C设备驱动。这3部分相互协作,形成了非常通用、可适应性很强的I2C框架。LinuxI2C体系结构进行分析,讲3个组成部分各自的功能节对LinuxI2C 进行分析,讲解i2c-core.c文件的功能和主要6.3节、6.4节分别详细介绍I2C总线驱动和I2C设备驱动的编写方法
6.5节、 ARM处理器I2C总线驱动以及挂接在I2C总线上的AT24XX系列EEPROM驱动LinuxLinux的I2CLinuxI2C体系结构分为3个组成部分 提供了I2C总线驱动和设备驱动的 、注销方法,I2C通信方法(即“algorithm”)15.1LinuxI2C体系I2C总线驱动I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU i2c_algorithm和控制I2C适配器产生通信信号的函经由I2C总线驱动的代码, 可以控制I2C适配器以主控方式产生开始位、停止位、读写周期,以及以从设备方式被读写、产生ACK等。I2C设备驱动I2C设备驱动(也称为客户驱动)是对I2C硬件体系结构中设备端的实现,设备一般CPU控制的I2CI2CCPU交换数据I2C设备驱动主要包含了数据结构i2c_driver和i2c_client, 在Linux2.6内核中,所有的I2C设备都在sysfs文件系统中显示,存于 $$tree|--|||||||||--0-0048-|--0-0049-|--0-004a-|--0-004b-|--0-004c-|--0-004d-|--0-004e-../../../devices/legacy/i2c-0/0-../../../devices/legacy/i2c-0/0-../../../devices/legacy/i2c-0/0-../../../devices/legacy/i2c-0/0-../../../devices/legacy/i2c-0/0-../../../devices/legacy/i2c-0/0-../../../devices/legacy/i2c-0/0-'--0-004f->../../../devices/legacy/i2c-0/0-'--'--lm75|--0-0048->../../../../devices/legacy/i2c-0/0-|--0-0049->../../../../devices/legacy/i2c-0/0-|--0-004a->../../../../devices/legacy/i2c-0/0-|--0-004b->../../../../devices/legacy/i2c-0/0-|--0-004c->../../../../devices/legacy/i2c-0/0-|--0-004d->../../../../devices/legacy/i2c-0/0-|--0-004e->../../../../devices/legacy/i2c-0/0-004e'--0-004f->../../../../devices/legacy/i2c-0/0- 地址的形式列出,例如在Linux内核源代码中的drivers 下包含一个i2c ,而在i2c i2c-core.c这个文件实现了 实现了I2C适配器设备文件的功能,每一个I2C适配器都被分配一个设备。通过适%d(件名并使用文件操作接口open()、write()、read()、ioctl()和close()等来 i2c-dev.c并没有针对特定的设备而设计,只是提供了通用的read()、write()和ioctl()等接口,应用层可以借用这些接口挂接在适配器上的I2C设备的空间或寄存器,并控I2C设备的工chips文件夹这个中包含了一些特定的I2C设备驱动,如Dallas公司的DS1337实时钟、EPSONRTC8564实时钟和I2C接口的EEPROM驱动等。在具体的I2C设备驱动中,调用的都是I2C 提供的API,因此,这使得具体的I2C设备驱动不依赖于CPU的类型和I2C适配器的硬件特性。busses这个文件中包含了一些I2C总线的驱动,如针对S3C2410、S3C2440和S3C6410等处理器I2C控制器驱动为i2c-s3c2410.calgos文件夹实现了一些I2C总线适配器的algorithm1123456789structi2c_adapterstructmodule*owner;/ i2c_adapter结构unsignedint /*algorithm的类型,定义于i2c-id.h,以I2C_ALGO_开始unsignedintstructi2c_algorithm*algo;/*总线通信方法结构体指针*/void*algo_data;/*algorithm数据*/int(*client_register)(structi2c_client*);时调用int*client_unregister)(structi2c_client**client注销时调用u8structsemaphore /*控制并structsemaphoreint的自旋锁int/*重试次数structdevicedev;/*适配器structclass_deviceclass_dev;/*类设备*/intnr;structlist_headclien;*client链表头structlist_headcharname[48];*适配器名称structcompletion/*用于同步1structi2c_algorithm2345678int(*master_xfer)(structi2c_adapter*adap,structi2c_msgintnum);*i2c传输函数指针int(*smbus_xfer)(structi2c_adapter*adap,u16unsignedshortflags,char/*smbus传输函数指针u8command,intsize,unioni2c_smbus_data*u32(*functionality)(structi2c_adapter*);/*返回适配器支持的功能此外,内核中i2c.h这个头文件i2c_driver、i2c_client、i2c_adapteri2c_algorithm4个数据结构进行了定义。理解这4个结构体的作用十分关键,代码15.1、15.2、15.3、15.4上述代码第4行对应为SMBus传输函数指针,SMBus大部分基于I2C总线规范,SMBus不需增加额外引脚。与I2C总线相比,SMBus增加了一些新的功能特性,在时序也有一定代 i2c_driver结构structi2c_driverintunsignedintint(*attach_adapter)(structi2c_adapter*);/*依附i2c_adapter函数指针int(*detach_adapter)(structi2c_adapter*);/*i2c_adapter函数指针int*detach_client)(structi2c_client*);/*i2cclient脱离函数指针地址unsigned地址unsignedshort /*7位 i2c_clientstructi2c_clientintint(*probe)(structi2c_client*,conststructi2c_device_idint(*remove)(structi2c_clientvoid(*shutdown)(structi2c_clientint(*suspend)(structi2c_client*,pm_message_tint(*resume)(structi2c_clientint(*command)(structi2c_client*client,unsignedintcmd,voidstructdevice_driverconststructi2c_device_id*id_table;*IDint(*detect)(structi2c_client*,intkind,structi2c_board_infoconststructi2c_client_address_datastructlist_head181111/*链表头/*用于structlist_headstructcompletioncharname[I2C_NAME_SIZE];*设备/*链表头/*用于structlist_headstructcompletioncharname[I2C_NAME_SIZE];*设备名称structi2c_adapter*adapter;/*依附i2c_adapter*/structi2c_driver*driver;/*i2c_driver*/structdevicedev;*设备结构体*/intirq;/*设备使用的中断号123456789i2c_adateti2ca i2c_adapter对应于物理上的一个适配器,而i2c_algorithm对应一套通信方法。一个I2C器需要i2c_algorithm中提供的通信函数来控制适配器上产生特定 周期。缺少的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用的i2c_algorithm的指i2c_algorithm中的关键函数master_xfer()用于产生I2C 周期需要的信号,以i2c_msg(即I2C消息)为单位。i2c_msg结构体也非常关键,代码 15.5给出了它的定义。structi2c_msg__u16structi2c_msg__u16addr;/*设备地址__u16flags;/*标志__u16len;/*消息长度__u8*buf;/*消息数据6i2c_driveri2c_client2c_diver对应一套驱动方法,其主要成员函数是pobe()reove()suspend()、esume()等,另外d_abe是该驱动所支持的I2C设备的Di2c_cient对应于真实的物理设备,每个2C设备都需要一个2c_cent来描述。i2c_diver与i2c_cient的关系是一对多,一个i2c_drver上可以支持多个同等类型的2c_cen。i2c_client信息通常在BSP的板文件中通过i2c_board_info填充,如下面代码就定义staticstructi2c_board_info _i2c_board_info[]=#ifdefined(CONFIG_JOYSTICK_AD7142)||staticstructi2c_board_info _i2c_board_info[]=#ifdefined(CONFIG_JOYSTICK_AD7142)||{I2C_BOARD_INFO("ad7142_joystick",.irq=}I2C总线驱动i2c_bus_typematch()i2c_device_match()中,会调i2c_match_id()函数匹配板文件中定义的ID和i2c_driver所支持的ID表。 ateri2c_clienti2c_ater与i2c_client的关系与I2C硬件体系中适配器和设备的关系一致,即i2c_client依附于i2c_ater。由于一个适配器上可以连接多个I2C设备,所以一个i2c_ater也可以被多个i2c_client依附,i2c_ater中包括依附于它的i2c_client的链表。I2C总线适配器上有两个使用相同驱动程序的yyyI2C设备,在打开该I2C总线的设备结点后相关数据结构之间的逻辑组织关系将如图15.2所示。从上面的分析可知,虽然I2C硬件体系结构比较简单,但是I2C体系结构在Linux中的实现竟有哪些是需要亲自做的,哪些是内核已经提供的呢?理清这个问题非常有意义,可以使面对具体一方面,适配器驱动可能是Linux内核本身还不包含的;另一方面,挂接在适配器上的具体nx I2C适配器的硬件驱动,探I2C适配器(如申请I2CI/O地址和中断号、驱动CPU控制的I2C适配器从硬件上产生各种信号以及处理I2C中断等。提供I2C适配器的algorithm,用具体适配器的_xfer()函数填充i2c_algorithmmaster_xfer指针,并把i2c_algorithmi2c_adapteralgo实现I2C设备驱动中的i2c_driver接口,用具体设备yyy的yyy_probe()、yyy_remove()、yyy_suspend()、yyy_resume()函数指针和i2c_device_id设备ID表赋值给i2c_driver的probe、remove、suspend、resume和id_table指针。实现2C设备所对应类型的具体驱动,i2c_diver只是实现设备与总线的挂接,而挂接在总线上的设备则是千差万别。例如,如果是字符设备,就实现文件操作接口,即实现具体设备yyy的yyy_read()、yyy_wie()和yyy_iocl()函数等;如果是声卡,就实现ALA驱动。上述工作中前两个属于I2C总线驱动,后两个属于I2C设备驱动,做完这些工作,系统会增加两个内核模块。15.3~15.4节将详细分析这些工作的实施方法,给出设计模15.5~15.615.2I2C驱动的各数据结Linux 11234567inti2c_attach_client(structi2c_client{代 clientattach/detachdevice_register(&client-}inti2c_detach_client(structi2c_client{13不需要被工程师修改,但是理解其中的主要函数非常关键,因为I2C总线驱动和设备驱动之间依赖于I2C inti2c_add_adapter(structi2cinti2c_add_adapter(structi2c_adapter*adap);inti2c_del_adapter(structi2c_adapter增加/删除i2c_driverintinti2c_register_driver(structmodule*owner,structi2c_driver*driver);inti2c_del_driver(structi2c_driver*driver);inlineinti2c_add_driver(structi2c_driverinti2c_attach_client(structi2c_clientinti2c_attach_client(structi2c_client*client);inti2c_detach_client(structi2c_client当一个具体的client被侦测到并被关联的时候,设备和sysfs文件将 。相反地,在被取消关联的时候,sysfs文件和设备也被注销,如代 15.6所示inti2c_transfer(structinti2c_transfer(structi2c_adapter*adap,structi2c_msg*msgs,intnum);inti2c_master_send(structi2c_client*client,constchar*buf,intcount);inti2c_master_recv(structi2c_client*client,char*buf,intcount);i2c_transfer()函数用于进行I2C适配器和I2C设备之间的一组消息交互,i2c_master_send()函数和i2c_master_recv()函 会调用i2c_transfer()函数分别完成一条写消息和一条读消息,如代15.7、代 15.8所示代 的i2c_master_send函inti2c_master_send(structi2c_client*client,constchar*buf,int{intstructi2c_adapter*adap=client-structi2c_msg/*构造一个写消息msg.addr=client-msg.flags=client->flags&msg.len=msg.buf=(char/*传输消息12123456789int{int*msgs,intif(adap->algo->master_xfer)retadap->algo->master_xfer(adap,msgs,num);*消息传输return}elsedev_dbg(&adap->dev,"I2Cleveltransfersnotsupported\n");return-ENOSYS;}141212ret=i2c_transfer(adap,&msg,14rern(ret==1)?count:15代 的i2c_master_recv函1inti2c_master_recv(structi2c_client*client,char*buf,int2{3structi2c_adapter*adap=client-4structi2c_msg5int6/*构造一个读消息7msg.addr=client-8msg.flags=client->flags&9msg.flags|=msg.len=msg.buf=/*传输消息ret=i2c_transfer(adap,&msg,/*成功(1条消息被处理)回读的字节数rern(ret==1)?count:}i2c_transfer()函数本身不具备驱动适配器物理硬件完成消息交互的能力,它只是寻找到i2c_adapter对应的i2c_algorithm,并使用i2c_algorithm的master_xfer()函数真正驱动硬件流程,如代码15.9所示。LinuxLinuxI2C总线驱动I2C适配器驱动加载与 总线驱动模块的加载函数要完成两个工作初始化I2C适配器所使用的硬件资源,如申请I/O地址、中1123456789staticint__init {_代 I2C总线驱动的模块加载和卸载函数模 }staticvoid__exit { 11通过i2c_add_adapter()添加i2c_adapter的数据结构,当然这个i2c_adapter数据结构的成 xx适配器的相应函数指针所初始化。 I2C适配器所使用的硬件资源, I/O地址、中断号等通过i2c_del_adapter()删除i2c_adapter的数据结构代 15.10所示为I2C适配器驱动的模块加载和卸载函数的模板上述代码 ater_hw_free()函数的实现都与具体的CPU和适配器硬件直接相关I2C总线通信方需要为特定的I2C适配器实现其通信方法,主要实现i2c_algorithm的master_xfer()函和functionality()函数 2_FUNC_I2C、master_xfer()函数在I2C适配器上完成传递给它的i2c_msg数组中的每个I2C消息,代15.11所示 设备的master_xfer()函数模板代 I2C总线驱动master_xfer函数模staticint _xfer(structi2c_adapter*adap,structi2c_msgint3for(i=0;i<num;i++) _start();/*产生开始位/*是读消息if(msgs[i]->flags&I2C_M_RD) _setaddr((msg->addr1)|1);*发送从设备读地址 _wait_ack();/*获得从设备的 _readbytes(msgs[i]->buf,msgs[i]->len); msgs[i]-长的数据到msgs[i]-else*是写消息 _setaddr(msg->addr<<1);/*发送从设备写地址 _wait_ack();/*获得从设备的 _writebytes(msgs[i]->buf,msgs[i]->len); msgs[i]-
长的数据到msgs[i]-21
_stop();/*产生停止位上述代码实际上给出了一个master_xfer()函数处理I2C消息数组的流程,对于数组中的每个消息,判断消息类型,若为读消息,则赋从设备地址为(msg->addr<<1)|1,否则msg->addr<<1。需产生一个停止位。如图15.3所示为整个master_xfer()完成的时序。15.3algorithmmaster_xfermaster_xfer函数模板中的i2c_adapter__start()、i2c_adapter__setaddr()、_wait_acki2c_adapter__readbytesi2c_adapter__writebytes()和i2c_adapter__stop()函数用于完成适配器的底层硬件操作,与I2C适配器和CPU的具体硬件直接相关,需要由工程师根据的来实现。 apter__writebytes()用于向从设备写入
AK12345_i2cstructi2c_msgunsignedint_i2cmaster_xfer()函数的实现在形式上会很多样,即便是Linux内核源代码中已经给出的一些I2C总线驱动的master_xfer()函数,由于由不同的组织或个人完成,风格上的差别也非常大,不一定能与模板完全对应,如master_xfer()函数模板给出的消息处理是顺序进行有的驱动以中断方式来完成这12345_i2cstructi2c_msgunsignedint_i2c;多数I2C总线驱动会定义一个_i2c结构体,作为i2c_adapter的algo_data(类似“私有数据,其中包含I2C消息数组指针、数组索引及I2C适配器algorithm 控制用的自旋锁、等待队列等,而master_xfer()函数完成消息数组中消息的处理也可通过对_i2c结构体相关成员的来控制。代码15.12所示为_i2c结构体的定义,与图15.2中的_i2c是对应的。;unsigned unsigned struct10
112234567static init{rern{exitLinuxI2C设备驱动I2C设备驱动要使用i2c_driver和i2c_client数据结构并填充i2c_driver中的成员函数。i2c_client一般被包含在设备的私有信息结构体yyy_data中,而i2c_driver则适合被定义为全局变量并初始化,代码15.13所示为已被初始化i2c_driver。代 已被初始化的staticstaticstructi2c_driveryyy_driver=.driver=.name= } = = =8LinuxI2C设备驱动的模块加载与I2C设备驱动的模块加载函数通用的方法是在I2C设备驱动的模块加载函数进行通过2c_add_drver(cdve 的i2c_del_driver()函数删除i2c_driver。代 15.14所示为I2C设备驱动的加载工作与LinuxI2C设备驱动的数在I2C设备上读写数据的时序和数据通常通过i2c_msg数组组织最后通过i2c_transfer()函数 15.15所示为一个 指定偏移offs寄存器的例子。代 I2C设备驱动数据传输范structi2c_msg/*第一条消息是写消息msg[0].addr=client-msg[0].flags=msg[0].len=676789msg[0].buf=msg[1].addrclient->addr;msg[1].flags=I2C_M_RD;msg[1].len=sizeof(buf);msg[1].buf=i2c_transfer(client->adapter,msg,Linux的i2c-dev.c文件i2c-dev.c文件完全可以被看作一个I2C设备驱动,不过,它实现的一个i2c_client是虚拟、临时的,随着设备文件的打开而产生,并随设备文件的关闭而撤销,并没有被添加到i2c_adapter的clien链表中。i2c-dev.cI2C适配器生成一个主设备号为89的设备文件i2c_driver的成员函数以及文件操作接口,所以i2c-dev.c的主体是“i2c_driver成员函数+字符设备驱动”。i2c-dev.c中提供i2cdev_read()、i2cdev_write()函数来对应用户空间要使用的read()和write()文件操作接口,这两个函数分别调用I2C的2c_aser_recv()和i2c_master_send()函数来构造一条I2C消息并适配器agorthm15.4所示的时序。但是,很遗憾,大多数稍微复杂一点2C设备的读写流程并不对应于一条消息,往往需要两条甚至的消息来进行一次读写周期(即如图15.5所示的重复开始位Repat模式,这种情况下,在应用层仍然调用ead()wte()文件API来读写2C设备,将不能正确地读写。许多工程师碰到过类似的问题,往往经过相当长时间的调试都没法解决2C设备的读写,连错误的原因也无法找到,显然是对2cdev_read()和2cdev_wre()函数的作用有所误解。开停止15.4i2cdev_readi2cdev_write函数对应开地数开地数停15.5RepStart模鉴于上述原因,2c-dev.c中i2cdev_ead()和2cdev_wte()函数不具备太强的通用性,没有太大的实用价值,只能适用于非Reptart模式的情况。对于两条以上消息组成的读写,在用户空间需要组织i2c_sg消息数组并调用I2C_RDWRIOCTL15.15所示为i2cdev_iocl函数的框架。staticinti2cdev_ioctl(structinode*inode,structfilestaticinti2cdev_ioctl(structinode*inode,structfileunsignedintcmd,unsignedlong3structi2c_client*client=(structi2c_client*)file-switch(cmd)casecase1123456789#include2#includefclh#include<unstd.#include<stdlib.h>#include#include<linux/i2c-intmain(intargc,char{unsignedintfd;unsignedshortmem_addr;unsignedshortsize;unsignedshortidx;#definecharcswap;{unsignedshortaddr;charbytes[2];}if(argc<3)printf("Use:\n%s/dev/i2c-xmem_addrsize\n",9 /*设置从设备地址casecasecasecasereturni2cdev_ioctl_rdrw(client,casecasecasereturn}return}常用的IOCTLI2C_SLAVE(设置从设备地址、I2C_RETRIES(没有ACK情况下的重试次数,默认为1)、I2C_TIMEOU(超时)以及I2C_RDWR。 15.16和代码 15.17所示为直接通过read()、write()接口和O_RDWRIOCTL读写I2C设备的例子。59return}sscanf(argv[2],"%d",&mem_addr);sscanf(argv[3],"%d",&size);if(size>BUFF_SIZE)size=BUFF_SIZE;fd=open(argv[1],O_RDWR);if(!fd){printf("Erroronopeningthedevicefile\n");return0;}ioctl(fd,I2C_SLAVE,0x50);*EEPROM地址*/ioctl(fd,I2C_TIMEOUT,1);/*设置超时*/ioctl(fd,I2C_RETRIES,1);/*设置重试次数*/for(idx=0;idx<size;++idx,{tmp.addr=mem_addr;cswap=tmp.bytes[0];tmp.bytes[0]=tmp.bytes[1];tmp.bytes[1]=cswap;write(fd,&tmp.addr,2);read(fd,&buf[idx],}buf[size]=return0;#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include<linux/i2c-intmain(intargc,char{structi2c_rdwr_ioctl_dataunsignedintunsignedintunsignedintslave_address,unsignedchar222intif(argc<4)printf("Usage:\n%s/dev/i2c-xstart_addrreg_addr\n",return2729fd=open(argv[1],O_RDWR);if(!fd)printf("Erroronopeningthedevicereturn34sscanf(argv[2],"%x",sscanf(argv[3],"%x",®_address);work_queue.nmsgs2;*消息work_queue.msgs=(structi2c_msg*)malloc(work_queue.nmsgsif(!work_queue.msgs)printf("Memoryallocreturn45ioctl(fd,I2C_TIMEOUT,2);*设置超时ioctl(fd,I2C_RETRIES,1);*设置重试次数for(i=reg_address;i<reg_address+16;i++)(work_queue.msgs[0]).addr=
(work_queue.msgs[1]).len=(work_queue.msgs[1]).flags=(work_queue.msgs[1]).addr=(work_queue.msgs[1]).buf=ret=ioctl(fd,I2C_RDWR,(unsignedlong)if(ret<printf("ErrorduringI2C_RDWRioctlwitherrorcode:%d\n",printf("reg:%02xval:%02x\n",i,}return#i2c-test/dev/i2c-00x180x20reg:20val:0769}序位于虚拟机的/home/lihacker/develop/svn/ldd6410-read-only/tes/i2c/i2c-base-test 用该工具可指定某I2C控制器上某I2C从设备的某寄存器,如读I2C控制器0上的地址为0x18的从设备,#i2c-test/dev/i2c-00x180x20reg:20val:07reg:2freg:2freg:21val:00reg:22val:00reg:23val:00reg:24val:00reg:25val:00reg:26val:00reg:27val:00reg:28val:00reg:29val:00reg:2aval:00reg:2bval:00reg:2cval:00reg:2dreg:2eS3C6410S3C6410I2C总线S3C6410I2C控制器硬件S3C6410处理 集成了一个I2C控制器,通过4个寄存器就可方便地对其进行控制,4个寄存器如下IICCON:I2C控制IICSTAT:I2C状态 IICADD:I2C地址寄存器S3C6410处理器集成的I2C控制器可支持主、从两种模式,主要使用其主模式。通IICCOCDSIICADD寄存器的操作,可在I2C总线上产生开始位、停止位、数据和地址,而传输的状态则通过IICSTAT寄存器获取。S3C6410I2C总线驱动总体S3C6410I2C总线驱动drivers/i2c/busses/i2c-s3c2410.cS3C24XX、S3C64XX、S5PC1XX和S5P64XX处理器,在使用的内核版本中,其名称仍然叫2410,显然是历史原因设计对应于i2c_adapter__init()模板的S3C6410的模块加载函数和对应于i2c_adapter__exit()函数模板的模块卸载函数。设计对应于 _xfer()模板的S3C6410适配器的通信方法函数针对S3C24XX、S3C64XX、S5PC1XXS5P64XX处理器,functionality()函数s3c24xx_i2c_func()只需简单地返回I2C_FUNC_I2C|I2C_FUNC_SMBUS_EMUL|I2C_FUNC_PROTOCOL_MANGLING表明其支持的功能。图15.6给出了S3C6410驱动中的主要函数与15.3节模板函数的对应关系,由于实1123456789staticint__init{代 S3C6410I2C总线驱动intret=ifif}return13staticvoid__exit{19法的方式不一样,模板的一个函数可能对应于S3C6410I2C总线驱动的多个函15.6I2C总线驱动模板与S3C6410I2C总线驱动S3C6410I2C适配器驱动的模块2C适配器驱动被作为一个单独的模块加载进内核,在模块的加载和卸载函数中,只需和注销一个patform_drver结构体,如代码 15.18所示。pafor_dver结构体包含了具体适配器的pobe(函数、emove(函数、resume(函数指针等信息,它需要被定义和赋值,如代码 15.19所示。 代 15.19 platform.driver结构 2=3=42=3=4=55=6=7="s3c2410-891staticints3c24xx_i2c_probe(structplatform_device2{代 S3C6410I2C总线驱动中的s3c24xx_i2c_probe函3structs3c24xx_i2c4structs3c2410_platform_i2c1staticints3c24xx_i2c_probe(structplatform_device2{代 S3C6410I2C总线驱动中的s3c24xx_i2c_probe函3structs3c24xx_i2c4structs3c2410_platform_i2c5structresource6int78pdata=pdev-9if(!pdata)dev_err(&pdev->dev,"noplatformreturn-}i2c=kzalloc(sizeof(structs3c24xx_i2c),if(!i2c)dev_err(&pdev->dev,"nomemoryforreturn-} i2c- =i2c- =i2c->adap.retries=i2c- =I2C_CLASS_HWMON|i2c- =27spin_lock_init(&i2c-30/*发现时钟并使能它32i2c->dev=&pdev-33i2c->clk=clk_get(&pdev->dev,34if(IS_ERR(i2c->clk)) dev_err(&pdev->dev,"cannotget ret=- goto3840dev_dbg(&pdev->dev,"clocksource%p\n",i2c->clk);42clk_enable(i2c-44res=platform_get_resource(pdev,IORESOURCE_MEM,if(res==NULL)dev_err(&pdev->dev,"cannotfindIOresource\n");ret=-ENOENT;goto}i2c->ioarea=request_mem_region(res->start,(res->end-res-pdev-if(i2c->ioarea==NULL)dev_err(&pdev->dev,"cannotrequestIO\n");ret=-ENXIO;goto}i2c->regs=ioremap(res->start,(res->end-res->start)+1);if(i2c->regs==NULL){dev_err(&pdev->dev,"cannotmapret=-goto}dev_dbg(&pdev->dev,"registers%p(%p,i2c->regs,i2c->ioarea,/*需要i2c-i2c->adap.dev.parent=&pdev-/*initialisethei2ccontrollerret=if(ret!=goto/*申请i2c->irq=ret=platform_get_irq(pdev,0);if(ret<=0){dev_err(&pdev->dev,"canntfindgoto}ret=request_irq(i2c->irq,s3c24xx_i2c_irq,IRQF_DISABLED,dev_name(&pdev->dev),i2c);if(ret!=0)dev_err(&pdev->dev,"cannotclaimIRQ%d\n",i2c->irq);gotoerr_iomap;12123456789staticints3c24xx_i2c_remove(structplatform_device{structs3c24xx_i2c*i2c=代 S3C6410I2C总线驱动中的s3c24xx_i2c_remove函free_irq(i2c->irq,i2c);clk_disable(i2c-clk_put(i2c-13iounmap(i2c-release_resource(i2c-kfree(i2c-return}9999ret=if(ret<0)dev_err(&pdev->dev,"failedtoregistercpufreqgoto105108110i2c->adap.nr=pdata->bus_num;112ret=i2c_add_numbered_adapter(&i2c-113if(ret<0) dev_err(&pdev->dev,"failedtoaddbustoi2c goto116118platform_set_drvdata(pdev,120dev_info(&pdev->dev,"%s:S3CI2Cadapter\n",i2c-121rern}上述代码中的主体工作是使能硬件并申请I2C适配器使用的I/O地址、中断号等,在这些作都完成无误后,通过I2C 提供的i2c_add_adapter()函数添加这个适配器。当处理器包含多个I2C控制器时,我 与 ove(函数,它在适配器块卸载函数调用platform_driver_unregister()函数时通过platform_driver的remove指针方式_i2c_remove()的设计模板如代 15.21所示1123456789structs3c24xx_i2cunsigned s3c24xx_i2c结构structunsignedintunsignedunsigned11unsigned 12unsignedint 14enum 15unsignedlong 17 structstructstructstruct#ifdefstruct代码15.20和代码15.21s3c24xx_i2c结构体进行适配器所有信息的封装,类似于私有信息结构体,它与代码15.12所示的_i2c结构体模板对应。代码15.22所示为s3c24xx_i2c结构体的定义。S3C6410I2C总线通信 15.20的第22行可以看出,I2C适配器对应的i2c_algorithm结构体实例为s3c24xx_ 15.23所示为s3c24xx_i2c_algorithm的定义。staticstructi2c_algorithms3c24xx_i2c_algorithm=staticstructi2c_algorithms3c24xx_i2c_algorithm= = =上述代码第一行指定了S3C6410I2C总线通信传输函数s3c24xx_i2c_xfer(),这个函数非常关键,所有I2C总线上对设备的最终应该由它来完成,代码15.24所示为这个重要函数以及其依赖的s3c24xx_i2c_doxfer()函数和s3c24xx_i2c_message_start()函数的源代码。代 S3C6410I2C总线驱动的master_xfer函staticints3c24xx_i2c_xfer(structi2c_adapterstructi2c_msg*msgs,int structs3c24xx_i2c*i2c=(structs3c24xx_i2c*)adap-5656789intintfor(retry=0;retry<adap->retries;{ret=s3c24xx_i2c_doxfer(i2c,msgs,num);if(ret!=-EAGAIN)returndev_dbg(i2c->dev,"Retryingtransmission(%d)\n",retry);}return-}staticints3c24xx_i2c_doxfer(structs3c24xx_i2cstructi2c_msg*msgs,int{unsignedlongtimeout;intret;intreturn-EIO;if(ret!=0){(i2c->dev,.getbuss3c24xx_i2c_stop(i2c,- %d\n,rtiicstat=readl(i2c->regs+if((iicstat&iicstat&=~(S3C2410_IICSTAT_TXRXEN| (iicstat,i2c->regs+}ret=-EAGAIN;goto}spin_lock_irq(&i2c-i2c-=i2c->msg_num=i2c->msg_ptr=i2c->msg_idx=i2c- =66spin_unlock_irq(&i2c-timeout=wait_event_timeout(i2c->wait,i2c->msg_num==0,Hz*ret=i2c-if(timeout==elseif(ret!=num)dev_dbg(i2c->dev,pletexfer(%d)\n",msleep(1);*确保停止位已经被return}staticvoids3c24xx_i2c_message_start(structs3c24xx_i2c*i2c,structi2c_msg*msg){unsignedintaddr=(msg->addr&0x7f)<<1;unsignedlongstat;unsignedlongstat=stat|=if(msg->flags&I2C_M_RD){stat|=}add=___stat|=if(msg->flags&I2C_M_REV_DIR_ADDR)addr^=1;iiccon=readl(i2c->regs+100wri (stat,i2c->regs+S3C2410_IICSTAT);102dev_dbg(i2c->dev,"START:%08lxtoIICSTAT,%02xtoDS\n",stat,103writeb(addr,i2c->regs+S3C2410_IICDS);105ndelay(i2c-107dev_dbg(i2c->dev,"iiccon,%08lx\n",108wri (iiccon,i2c->regs+S3C2410_IICCON);110stat|=111 (stat,i2c->regs+多可以重试adap->retriess3c24xx_i2c_doxfer()首先将S3C6410的I2C适配器设置为I2C始化结构体,使能I2C中断,并调用s3c24xx_i2c_message_start()函数启动I2C消息的传输s3c24xx_i2c_message_start()函数写S3C6410适配器对应的控制寄存器,向I2C从设备传递开始上述代码只是启动了I2C消息数组的传输周期,并没有完整实现图15.3中给出的algorithmmaster_xfer流程。这个流程的完整实现需要借助I2C适配器上的中断来步步推进。代 S3C6410I2C适配器中断处理函数i2s_s3c_irq_nextbyte()函数的源代码代 S3C6410I2C适配器中断处理函staticirqreturn_ts3c24xx_i2c_irq(intirqno,void{structs3c24xx_i2c*i2c=unsignedlongunsignedlongtmp;status=readl(i2c->regs+if(status&S3C2410_IICSTAT_ARBITR)}if(i2c->state==STATE_IDLE)tmp=readl(i2c->regs+tmp&= (tmp,i2c->regs+goto
i2s_s3c_irq_nextbyte(i2c,status);/*把传输工作进一步推进return staticinti2s_s3c_irq_nextbyte(structs3c24xx_i2c*i2c,unsignedlong{unsignedlongunsignedcharintret=switch(i2c->state)casegotocasegotocase 最近做的一件事是启动一个新I2C消息if(iicstat&S3C2410_IICSTAT_LASTBIT!(i2c->msg->flags&I2C_M_IGNORE_NAK))/*没有收到ACKs3c24xx_i2c_stop(i2c,-goto if(i2c->msg->flags&i2c->state=i2c->statei2c->state= 仅一条消息,而且长度为0(主要用于适配器探测),发送停止位if(is_lastmsg(i2c)&&i2c->msg->len==0)s3c24xx_i2c_stop(i2c,goto if(i2c->state==goto/*进入写状态caseif(!is_msgend(i2c))byte=i2c->msg->buf[i2c-writeb(byte,i2c->regs+ndelay(i2c-}elseif(!is_lastmsg(i2c))/*推进到下一i2c->msg_ptr=icic /*检查是否要为该消息产if(i2c->msg->flags&I2C_M_NOSTART)if(i2c->msg->flags&I2C_M_RD)s3c24xx_i2c_stop(i2c,- goto}else/*发送新的开始位s3c24xx_i2c_message_start(i2c,i2c-i2c->state= }elses3c24xx_i2c_stop(i2c,0);/*sendstop case/*有一个字节可读,看是否还有消息要if(!(i2c->msg->flags&I2C_M_IGNORE_NAK)if(iicstat&S3C2410_IICSTAT_LASTBIT)dev_dbg(i2c->dev,"READ:NoAck\n");s3c24xx_i2c_stop(i2c,-goto byte=readb(i2c->regs+i2c->msg->buf[i2c->msg_ptr++]=byte;if(is_msglast(i2c)){/*lastbyteofbufferif}elseif(is_msgend(i2c))/*还有消息要处理吗if(is_lastmsg(i2c))s3c24xx_i2c_stop(i2c,0);/*lastmessage,sendstopandcomplete}else/*推进到下一i2c->msg_ptr=i2c-i2c- 119}/*irq清除tmp=readl(i2c->regs+tmp&= (mpti2cres+31ICON) baoreturn}中断处理函数s3c24xx_i2c_irq()主要通过调用i2s_s3c_irq_nextbyte()函数进行传输工作的进一步推进。i2s_s3c_irq_nextbyte()函数通过switch(i2c->state)语句分成i2c->state的不同状态进行处理,证明有误,直接
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年贺兰县带编教师招聘考试模拟试题及答案解析
- 2026年建水县带编教师招聘考试备考试题及答案解析
- 2026年望谟县带编教师招聘笔试备考试题及答案解析
- 2026-2027学年鹰潭市中考数学仿真试卷(含答案解析)
- 2026年壤塘县带编教师招聘考试备考试题及答案解析
- 2026年乐东黎族自治县带编教师招聘考试模拟试题及答案解析
- 2026年煤矿安全规程知识考试模拟试题及答案(共十三套)
- 2026年叶县带编教师招聘笔试模拟试题及答案解析
- 合肥工业大学-电路真题及答案
- 2026年滑县带编教师招聘考试备考题库及答案解析
- 铁路卫生间改造施工方案
- 油田水平井施工方案
- 幼儿园教师师德考核档案模板
- 2025年新安全生产法知识竞赛试题库及答案
- 2025年教师资格考试初级中学学科知识与教学能力化学试题与参考答案
- 中央八项规定精神应知应会100题测试卷完整答案详解
- 统编版(2024)七年级上册道德与法治全册教案
- 健康饮食:平衡饮食的益处
- 床上用品采购 投标方案
- 2025年浙江省省级职工职业技能竞赛(工业机器人系统运维员赛项)考试题库(含答案)
- 第5章 聚类分析幻灯片
评论
0/150
提交评论