您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 信息化管理 > SPI驱动框架源码分析
SPI驱动框架源码分析2013-04-1216:13:08分类:LINUXSPI驱动框架源码分析SPI协议是一种同步的串行数据连接标准,由摩托罗拉公司命名,可工作于全双工模式。相关通讯设备可工作于m/s模式。主设备发起数据帧,允许多个从设备的存在。每个从设备有独立的片选信号,SPI一般来说是四线串行总线结构。接口:SCLK——SerialClock(outputfrommaster)时钟(主设备发出)MOSI/SIMO——MasterOutput,SlaveInput(outputfrommaster)数据信号线mosi(主设备发出)MISO/SOMI——MasterInput,SlaveOutpu(outputfromslave)数据信号线(从设备)SS——SlaveSelect(activelow;outputfrommaster)片选信号下面来看一下Linux中的SPI驱动。在Linux设备驱动框架的设计中,有一个重要的主机,外设驱动框架分离的思想,如下图。外设a,b,c的驱动与主机控制器A,B,C的驱动不相关,主机控制器驱动不关心外设,而外设驱动也不关心主机,外设只是访问核心层的通用的API进行数据的传输,主机和外设之间可以进行任意的组合。如果我们不进行如图的主机和外设分离,外设a,b,c和主机A,B,C进行组合的时候,需要9种不同的驱动。设想一共有m个主机控制器,n个外设,分离的结构是需要m+n个驱动,不分离则需要m*n个驱动。下面介绍spi子系统的数据结构:在Linux中,使用spi_master结构来描述一个SPI主机控制器的驱动。[html]viewplaincopy1.structspi_master{2.structdevicedev;/*总线编号,从0开始*/3.s16bus_num;/*支持的片选的数量,从设备的片选号不能大于这个数量*/4.u16num_chipselect;5.u16dma_alignment;/*改变spi_device的特性如:传输模式,字长,时钟频率*/6.int(*setup)(structspi_device*spi);/*添加消息到队列的方法,这个函数不可睡眠,他的任务是安排发生的传送并且调用注册的回调函数complete()*/7.int(*transfer)(structspi_device*spi,structspi_message*mesg);8.void(*cleanup)(structspi_device*spi);9.};分配,注册和注销的SPI主机的API由SPI核心提供:[cpp]viewplaincopy1.structspi_master*spi_alloc_master(structdevice*host,unsignedsize);2.intspi_register_master(structspi_master*master);3.voidspi_unregister_master(structspi_master*master);在Linux中用spi_driver来描述一个SPI外设驱动。[cpp]viewplaincopy1.structspi_driver{2.int(*probe)(structspi_device*spi);3.int(*remove)(structspi_device*spi);4.void(*shutdown)(structspi_device*spi);5.int(*suspend)(structspi_device*spi,pm_message_tmesg);6.int(*resume)(structspi_device*spi);7.structdevice_driverdriver;8.};可以看出,spi_driver结构体和platform_driver结构体有极大的相似性,都有probe(),remove(),suspend(),resume()这样的接口。Linux用spi_device来描述一个SPI外设设备。[cpp]viewplaincopy1.structspi_device{2.structdevicedev;3.structspi_master*master;//对应的控制器指针u324.max_speed_hz;//spi通信的时钟u85.chip_select;//片选,用于区分同一总线上的不同设备6.u8mode;7.#defineSPI_CPHA0x01/*clockphase*/8.#defineSPI_CPOL0x02/*clockpolarity*/9.#defineSPI_MODE_0(0|0)/*(originalMicroWire)*/#defineSPI_MODE_1(0|SPI_CPHA)10.#defineSPI_MODE_2(SPI_CPOL|0)11.#defineSPI_MODE_3(SPI_CPOL|SPI_CPHA)#defineSPI_CS_HIGH0x04/*chipselectactivehigh?*/12.#defineSPI_LSB_FIRST0x08/*per-wordbits-on-wire*/13.#defineSPI_3WIRE0x10/*SI/SOsignalsshared*/14.#defineSPI_LOOP0x20/*loopbackmode*/15.u8bits_per_word;//每个字长的比特数16.intirq;//使用的中断17.void*controller_state;18.void*controller_data;19.charmodalias[32];//名字20.};如下图,看这三个结构的关系,这里spi_device与spi_master是同一个父设备,这是在spi_new_device函数中设定的,一般这个设备是一个物理设备。这里的spi_master_class,spi_bus_type又是什么呢,看下边两个结构体:[cpp]viewplaincopy1.structbus_typespi_bus_type={2..name=spi,3..dev_attrs=spi_dev_attrs,4..match=spi_match_device,5..uevent=spi_uevent,6..suspend=spi_suspend,7..resume=spi_resume,8.};9.staticstructclassspi_master_class={10..name=spi_master,11..owner=THIS_MODULE,12..dev_release=spi_master_release,13.};spi_bus_type对应spi中的spibus总线,spidev的类定义如下:[cpp]viewplaincopy1.staticstructclass*spidev_class;创建这个类的主要目的是使mdev/udev能在/dev下创建设备节点/dev/spiB.C。B代表总线,C代表片外设备的片选号。下边来看两个板级的结构,其中spi_board_info用来初始化spi_device,s3c2410_spi_info用来初始化spi_master。这两个板级的结构需要在移植的时候在arch/arm/mach-s3c2440/mach-smdk2440.c中初始化。[cpp]viewplaincopy1.structspi_board_info{2.charmodalias[32];//设备与驱动匹配的唯一标识3.constvoid*platform_data;4.void*controller_data;5.intirq;6.u32max_speed_hz;7.u16bus_num;//设备所归属的总线编号8.u16chip_select;9.u8mode;10.};11.structs3c2410_spi_info{12.intpin_cs;//芯片选择管脚13.unsignedintnum_cs;//总线上的设备数14.intbus_num;//总线号15.void(*gpio_setup)(structs3c2410_spi_info*spi,intenable);//spi管脚配置函数16.void(*set_cs)(structs3c2410_spi_info*spi,intcs,intpol);17.};boardinfo是用来管理spi_board_info的结构,spi_board_info通过spi_register_board_info(structspi_board_infoconst*info,unsignedn)交由boardinfo来管理,并挂到board_list链表上,list_add_tail(&bi-list,&board_list);[cpp]viewplaincopy1.structboardinfo{2./*用于挂到链表头board_list上*/3.structlist_headlist;4./*管理的spi_board_info的数量*/5.unsignedn_board_info;6./*存放结构体spi_board_info*/7.structspi_board_infoboard_info[0];8.};s3c24xx_spi是S3C2440的SPI控制器在Linux内核中的具体描述,该结构包含spi_bitbang内嵌结构,控制器时钟频率和占用的中断资源等重要成员,其中spi_bitbang具体负责SPI数据的传输。[cpp]viewplaincopy1.structs3c24xx_spi{2./*bitbanghastobefirst*/3.structspi_bitbangbitbang;4.structcompletiondone;5.void__iomem*regs;6.intirq;7.intlen;8.intcount;9.void(*set_cs)(structs3c2410_spi_info*spi,intcs,intpol);10./*databuffers*/constunsignedchar*tx;11.unsignedchar*rx;12.structclk*clk;13.structresource*ioarea;14.structspi_master*master;15.structspi_device*curdev;16.structdevice*dev;17.structs3c2410_spi_info*pdata;18.};为了解决多个不同的SPI设备共享SPI控制器而带来的访问冲突,spi_bitbang使用内核提供的工作队列(workqueue)。workqueue是Linux内核中定义的一种回调处理方式。采用这种方式需要传输数据时,不直接完成数据的传输,而是将要传输的工作分装成相应的消息(spi_message),发送给对应的workqueue,由与workqueue关联的内核守护线程(daemon)负责具体的执行。由于workqueue会将收到的消息按时间先后顺序排列,这样就是对设备的访问严格串行化,解决了冲突。[html]viewplaincopy1.structspi_bitbang{2.structworkqueue_struct*workqueue;//工作队列头3.structwork_structwork;//每一次传输都传递下来一个spi_message,都向工作队列头添加一个4.workspinlock_tlock;5.structlist_headqueue;//挂接spi_message,如果上一次的spi_message还没有处理完,接下来的spi_message就挂接在queue上等待处理6.u8busy;//忙碌标志7.u8use_dma;8.
本文标题:SPI驱动框架源码分析
链接地址:https://www.777doc.com/doc-2859762 .html