SPI驱动框架源码_第1页
SPI驱动框架源码_第2页
SPI驱动框架源码_第3页
SPI驱动框架源码_第4页
SPI驱动框架源码_第5页
已阅读5页,还剩32页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、SPI驱动框架源码分析2013-04-12 16:13:08分类:LINUXSPI驱动框架源码分析SPI协议是一种同步的串行数据连接标准,由摩托罗拉公司命名,可工作于全双工模式。相关通讯设备可工作于m/s模式。主设备发起数据帧,允许多个从设备的存在。每个从设备有独立的片选信号,SPI一般来说是四线串行总线结构。接口:SCLKSerial Clock(output from master)时钟(主设备发出)MOSI/SIMOMaster Output, Slave Input(output from master)数据信号线mosi(主设备发出)MISO/SOMIMaster Input,Sla

2、ve Outpu(output from slave)数据信号线(从设备)SSSlave Select(active low;output from master)片选信号下面来看一下Linux中的SPI驱动。在Linux设备驱动框架的设计中,有一个重要的主机,外设驱动框架分离的思想,如下图。外设a,b,c的驱动与主机控制器A,B,C的驱动不相关,主机控制器驱动不关心外设,而外设驱动也不关心主机,外设只是访问核心层的通用的API进行数据的传输,主机和外设之间可以进行任意的组合。如果我们不进行如图的主机和外设分离,外设a,b,c和主机A,B,C进行组合的时候,需要9种不同的驱动。设想一共有m个主

3、机控制器,n个外设,分离的结构是需要m+n个驱动,不分离则需要m*n个驱动。下面介绍spi子系统的数据结构:在Linux中,使用spi_master结构来描述一个SPI主机控制器的驱动。htmlview plaincopy1. struct spi_master 2. struct device dev;/*总线编号,从0开始*/3. s16 bus_num;/*支持的片选的数量,从设备的片选号不能大于这个数量*/4. u16 num_chipselect;5. u16 dma_alignment;/*改变spi_device的特性如:传输模式,字长,时钟频率*/6. int (*setup)

4、(struct spi_device *spi);/*添加消息到队列的方法,这个函数不可睡眠,他的任务是安排发生的传送并且调用注册的回调函数complete()*/7. int (*transfer)(struct spi_device *spi,struct spi_message *mesg);8. void (*cleanup)(struct spi_device *spi);9. ;分配,注册和注销的SPI主机的API由SPI核心提供:cppview plaincopy1. structspi_master *spi_alloc_master(structdevice *host, u

5、nsigned size);2. intspi_register_master(structspi_master *master);3. voidspi_unregister_master(structspi_master *master);在Linux中用spi_driver来描述一个SPI外设驱动。cppview plaincopy1. structspi_driver 2. int (*probe)(structspi_device *spi);3. int (*remove)(structspi_device *spi);4. void (*shutdown)(structspi_de

6、vice *spi);5. int (*suspend)(structspi_device *spi, pm_message_t mesg);6. int (*resume)(structspi_device *spi);7. structdevice_driver driver;8. ;可以看出,spi_driver结构体和platform_driver结构体有极大的相似性,都有probe(),remove(),suspend(),resume()这样的接口。Linux用spi_device来描述一个SPI外设设备。cppview plaincopy1. structspi_device 2

7、. structdevice dev;3. structspi_master *master;/对应的控制器指针u324. max_speed_hz;/spi通信的时钟u85. chip_select;/片选,用于区分同一总线上的不同设备6. u8 mode;7. #define SPI_CPHA 0x01 /* clock phase */8. #define SPI_CPOL 0x02 /* clock polarity */9. #define SPI_MODE_0 (0|0) /* (original MicroWire) */#define SPI_MODE_1 (0|SPI_CPH

8、A)10. #define SPI_MODE_2 (SPI_CPOL|0)11. #define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)#define SPI_CS_HIGH 0x04 /* chipselect active high? */12. #define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */13. #define SPI_3WIRE 0x10 /* SI/SO signals shared */14. #define SPI_LOOP 0x20 /* loopback mode */15. u8 bits

9、_per_word;/每个字长的比特数16. int irq;/使用的中断17. void *controller_state;18. void *controller_data;19. char modalias32;/名字20. ;如下图,看这三个结构的关系,这里spi_device与spi_master是同一个父设备,这是在spi_new_device函数中设定的,一般这个设备是一个物理设备。这里的spi_master_class,spi_bus_type又是什么呢,看下边两个结构体:cppview plaincopy1. structbus_type spi_bus_type = 2.

10、 .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中的spi

11、 bus总线,spidev的类定义如下:cppview plaincopy1. 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中初始化。cppview plaincopy1. structspi_

12、board_info 2. char modalias32;/设备与驱动匹配的唯一标识3. constvoid *platform_data;4. void *controller_data;5. int irq;6. u32 max_speed_hz;7. u16 bus_num;/设备所归属的总线编号8. u16 chip_select;9. u8 mode;10. ;11. structs3c2410_spi_info 12. int pin_cs;/芯片选择管脚13. unsignedint num_cs;/总线上的设备数14. int bus_num;/总线号15. void(*gp

13、io_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(struct spi_board_info const *info, unsigned n)交由boardinfo来管理,并挂到board_list链表上,list_add_tail(&bi-list,&bo

14、ard_list);cppview plaincopy1. structboardinfo 2. /*用于挂到链表头board_list上*/3. structlist_head list;4. /*管理的spi_board_info的数量*/5. unsigned n_board_info;6. /*存放结构体spi_board_info*/7. structspi_board_info board_info0;8. ;s3c24xx_spi是S3C2440的SPI控制器在Linux内核中的具体描述,该结构包含spi_bitbang内嵌结构,控制器时钟频率和占用的中断资源等重要成员,其中sp

15、i_bitbang具体负责SPI数据的传输。cppview plaincopy1. structs3c24xx_spi 2. /* bitbang has to be first */3. structspi_bitbang bitbang;4. structcompletion done;5. void_iomem *regs;6. int irq;7. int len;8. int count;9. void (*set_cs)(structs3c2410_spi_info *spi,intcs,intpol);10. /* data buffers */constunsignedchar

16、*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内核中定义的一种回调处理方式。采用这种方式需要传输数据时,不

17、直接完成数据的传输,而是将要传输的工作分装成相应的消息(spi_message),发送给对应的workqueue,由与workqueue关联的内核守护线程(daemon)负责具体的执行。由于workqueue会将收到的消息按时间先后顺序排列,这样就是对设备的访问严格串行化,解决了冲突。htmlview plaincopy1. struct spi_bitbang 2. struct workqueue_struct *workqueue; /工作队列头3. struct work_struct work; /每一次传输都传递下来一个spi_message,都向工作队列头添加一个4. works

18、pinlock_t lock;5. struct list_head queue; /挂接spi_message,如果上一次的spi_message还没有处理完,接下来的spi_message就挂接在queue上等待处理6. u8 busy; /忙碌标志7. u8 use_dma;8. u8 flags;9. struct spi_master *master;/*一下3个函数都是在函数s3c24xx_spi_probe()中被初始化*/10. int (*setup_transfer)(struct spi_device *spi,struct spi_transfer *t); /设置传输

19、模式11. void (*chipselect)(struct spi_device *spi, int is_on); /片选12. #define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */13. #define BITBANG_CS_INACTIVE 0/*传输函数,由s3c24xx_spi_txrx来实现*/14. int (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t);15. u32 (*txrx_word4)(struct spi_device *sp

20、i,unsigned nsecs,u32 word, u8 bits);16. ;下面来看看spi_message:cppview plaincopy1. structspi_message 2. structlist_head transfers;/此次消息的传输队列,一个消息可以包含多个传输段3. structspi_device *spi;/传输的目的设备4. unsigned is_dma_mapped:1;/如果为真,此次调用提供dma和cpu虚拟地址5. void (*complete)(void*context);/异步调用完成后的回调函数6. void *context;/回调

21、函数的参数7. unsigned actual_length;/此次传输的实际长度8. int status;/执行的结果,成功被置0,否则是一个负的错误码9. structlist_head queue;10. void *state;11. ;在有消息需要传递的时候,会将spi_transfer通过自己的transfer_list字段挂到spi_message的transfers链表头上。spi_message用来原子的执行spi_transfer表示的一串数组传输请求。这个传输队列是原子的,这意味着在这个消息完成之前不会有其他消息占用总线。消息的执行总是按照FIFO的顺序。下面看一看sp

22、i_transfer:cppview plaincopy1. structspi_transfer 2. constvoid*tx_buf;/要写入设备的数据(必须是dma_safe),或者为NULL3. void *rx_buf;/要读取的数据缓冲(必须是dma_safe),或者为NULL4. unsigned len;/tx和rx的大小(字节数),这里不是指它的和,而是各自的长度,他们总是相等的5. dma_addr_t tx_dma;/如果spi_message.is_dma_mapped是真,这个是tx的dma地址6. dma_addr_t rx_dma;/如果spi_message.

23、is_dma_mapped是真,这个是rx的dma地址7. unsigned cs_change:1;/影响此次传输之后的片选,指示本次tranfer结束之后是否要重新片选并调用setup改变设置,这个标志可以较少系统开销u88. bits_per_word;/每个字长的比特数,如果是0,使用默认值9. u16 delay_usecs;/此次传输结束和片选改变之间的延时,之后就会启动另一个传输或者结束整个消息10. u32 speed_hz;/通信时钟。如果是0,使用默认值11. structlist_head transfer_list;/用来连接的双向链表节点Linux驱动修炼之道-SPI

24、驱动框架源码分析(中)分类:linux驱动编程2011-06-29 09:532150人阅读评论(13)收藏举报努力成为linux kernel hacker的人李万鹏原创作品,为梦而战。转载请标明出处这篇来分析spi子系统的建立过程。嵌入式微处理器访问SPI设备有两种方式:使用GPIO模拟SPI接口的工作时序或者使用SPI控制器。使用GPIO模拟SPI接口的工作时序是非常容易实现的,但是会导致大量的时间耗费在模拟SPI接口的时序上,访问效率比较低,容易成为系统瓶颈。这里主要分析使用SPI控制器的情况。这个是由sys文件系统导出的spi子系统在内核中的视图了。首先了解一下Linux内核中的几个

25、文件:spi.c也就是spi子系统的核心了,spi_s3c24xx.c是s3c24xx系列芯片的SPI controller驱动,它向更上层的SPI核心层(spi.c)提供接口用来控制芯片的SPI controller,是一个被其他驱动使用的驱动。而spidev.c是在核心层基础之上将SPI controller模拟成一个字符型的驱动,向文件系统提供标准的文件系统接口,用来操作对应的SPI controller。下面我们来看看spi子系统是怎么注册进内核的:cppview plaincopy1. staticint_init spi_init(void)2. 3. intstatus;4. b

26、uf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);5. if(!buf) 6. status = -ENOMEM;7. gotoerr0;8. 9. status = bus_register(&spi_bus_type);10. if(status 0)11. gotoerr1;12. status = class_register(&spi_master_class);13. if(status dev, sizeof(struct s3c24xx_spi);10. if (master= NULL) 11. dev_err(&pdev-dev, No memory

27、 for spi_mastern);12. err= -ENOMEM;13. goto err_nomem;14. 15. /*从master中获得s3c24xx_spi*/16. hw=spi_master_get_devdata(master);17. memset(hw, 0, sizeof(struct s3c24xx_spi);18. 19. 20. hw-master=spi_master_get(master);21. /*驱动移植的时候需要实现的重要结构,初始化为&s3c2410_spi0_platdata*/22. hw-pdatapdata= pdata = pdev-de

28、v.platform_data;23. hw-dev= &pdev-dev;24. 25. 26. if (pdata= NULL) 27. dev_err(&pdev-dev, No platform data suppliedn);28. err= -ENOENT;29. goto err_no_pdata;30. 31. /*设置平台的私有数据为s3c24xx_spi*/32. platform_set_drvdata(pdev, hw);33. init_completion(&hw-done);34. 35. 36. /* setup the master state. */37.

29、/*该总线上的设备数*/38. master-num_chipselect=hw-pdata-num_cs;39. /*总线号*/40. master-bus_num=pdata-bus_num;41. 42. 43. /* setup the state for the bitbang driver */44. /*spi_bitbang专门负责数据的传输*/45. hw-bitbang.master =hw-master;46. hw-bitbang.setup_transfer=s3c24xx_spi_setupxfer;47. hw-bitbang.chipselect =s3c24x

30、x_spi_chipsel;48. hw-bitbang.txrx_bufs =s3c24xx_spi_txrx;49. hw-bitbang.master-setup =s3c24xx_spi_setup;50. 51. 52. dev_dbg(hw-dev, bitbang at %pn, &hw-bitbang);53. 54. 。55. 56. /*初始化设置寄存器,包括对SPIMOSI,SPIMISO,SPICLK引脚的设置*/57. s3c24xx_spi_initialsetup(hw);58. 59. 60. /* register our spi controller */6

31、1. 62. 63. err=spi_bitbang_start(&hw-bitbang);64. 。65. spi controller的register在spi_bitbang_start函数中实现:htmlview plaincopy1. int spi_bitbang_start(struct spi_bitbang *bitbang)2. 3. int status;4. 5. 6. if (!bitbang-master | !bitbang-chipselect)7. return -EINVAL;8. /*动态创建一个work_struct结构,它的处理函数是bitbang_w

32、ork*/9. INIT_WORK(&bitbang-work, bitbang_work);10. spin_lock_init(&bitbang-lock);11. INIT_LIST_HEAD(&bitbang-queue);12. /*spi的数据传输就是用这个方法*/13. if (!bitbang-master-transfer)14. bitbang-master-transfer=spi_bitbang_transfer;15. if (!bitbang-txrx_bufs) 16. bitbang-use_dma=0;17. /*spi_s3c24xx.c中有spi_bitb

33、ang_bufs方法,在bitbang_work中被调用*/18. bitbang-txrx_bufs=spi_bitbang_bufs;19. if (!bitbang-master-setup) 20. if (!bitbang-setup_transfer)21. bitbang-setup_transfer=22. spi_bitbang_setup_transfer;23. /*在spi_s3c24xx.c中有setup的处理方法,在spi_new_device中被调用*/24. bitbang-master-setup=spi_bitbang_setup;25. bitbang-m

34、aster-cleanup=spi_bitbang_cleanup;26. 27. else if (!bitbang-master-setup)28. return -EINVAL;29. 30. 31. /* this task is the only thing to touch the SPI bits */32. bitbang-busy=0;33. /调用create_singlethread_workqueue创建单个工作线程/34. bitbang-workqueue=create_singlethread_workqueue(35. dev_name(bitbang-mast

35、er-dev.parent);36. if (bitbang-workqueue= NULL) 37. status= -EBUSY;38. goto err1;39. 40. status=spi_register_master(bitbang-master);41. if (statusworkqueue);46. err1:47. return status;48. 然后看这里是怎样注册spi主机控制器驱动的:cppview plaincopy1. intspi_register_master(structspi_master *master)2. 3. 。4. /*将spi添加到内核,

36、这也是sys/class/Spi_master下产生Spi0,Spi1的原因*/5. dev_set_name(&master-dev,spi%u, master-bus_num);6. status = device_add(&master-dev);7. scan_boardinfo(master);8. 这里跟踪scan_boardinfo函数:cppview plaincopy1. staticvoidscan_boardinfo(structspi_master *master)2. 3. structboardinfo *bi;4. mutex_lock(&board_lock);

37、5. /*遍历所有挂在board_list上的struct boardinfo*/6. list_for_each_entry(bi, &board_list, list) 7. structspi_board_info *chip = bi-board_info;8. unsigned n;9. /*遍历每个boardinfo管理的spi_board_info,如果设备的总线号与控制器的总线好相等,则创建新设备*/10. for(n = bi-n_board_info; n 0; n-, chip+) 11. if(chip-bus_num != master-bus_num)12. con

38、tinue;13. (void) spi_new_device(master, chip);14. 15. 16. mutex_unlock(&board_lock);17. 在移植的时候我们会在mach-smdk2440.c中的smdk2440_machine_init中添加spi_register_board_info这个函数完成了将spi_board_info交由boardinfo管理,并把boardinfo挂载到board_list链表上。也就是说在系统初始化的时候将spi_device交由到挂在board_list上的boardinfo管理,在spi controller的drive

39、r注册的时候不但注册这个主机控制器的驱动,还要遍历这个主机控制器的总线上的spi_device,将总线上的spi_device全部注册进内核。当注册进内核并且spi_driver已经注册的时候,如果总线match成功,则会调用spi_driver的probe函数,这个将在后边进行分析。htmlview plaincopy1. int _init2. spi_register_board_info(struct spi_board_info const *info, unsigned n)3. 4. struct boardinfo *bi;5. 6. bi=kmalloc(sizeof(*bi

40、) + n * sizeof *info, GFP_KERNEL);7. if (!bi)8. return -ENOMEM;9. bi-nn_board_info= n;10. memcpy(bi-board_info, info, n * sizeof *info);11. 12. 13. mutex_lock(&board_lock);14. list_add_tail(&bi-list, &board_list);15. mutex_unlock(&board_lock);16. return 0;17. 看一下创建新设备的函数:htmlview plaincopy1. struct

41、spi_device *spi_new_device(struct spi_master *master,2. struct spi_board_info *chip)3. 4. struct spi_device *proxy;5. int status;6. proxy=spi_alloc_device(master);7. if (!proxy)8. return NULL;9. 10. 11. WARN_ON(strlen(chip-modalias)= sizeof(proxy-modalias);12. /*初始化spi_device的各个字段*/13. proxy-chipchi

42、p_select= chip-chip_select;14. proxy-max_speed_hz=chip-max_speed_hz;15. proxy-mode=chip-mode;16. proxy-irq=chip-irq;17. /*这里获得了spi_device的名字,这个modalias也是在我们移植时在mach-smdk2440.c中的s3c2410_spi0_board中设定的*/18. strlcpy(proxy-modalias, chip-modalias, sizeof(proxy-modalias);19. proxy-dev.platform_data= (voi

43、d *) chip-platform_data;20. proxy-controller_data=chip-controller_data;21. proxy-controller_state=NULL;22. /*主要完成将spi_device添加到内核*/23. status=spi_add_device(proxy);24. if (statusdev的一些字段。cppview plaincopy1. structspi_device *spi_alloc_device(structspi_master *master)2. 3. structspi_device *spi;4. structdevice *dev = master-dev.parent;5. if(!spi_master_get(master)6. returnNUL

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论