Chinaunix首页 | 论坛 | 博客
  • 博客访问: 855965
  • 博文数量: 290
  • 博客积分: 511
  • 博客等级: 下士
  • 技术积分: 1590
  • 用 户 组: 普通用户
  • 注册时间: 2010-11-29 13:14
文章分类

全部博文(290)

文章存档

2018年(28)

2017年(19)

2016年(8)

2015年(1)

2014年(14)

2013年(12)

2012年(22)

2011年(186)

分类:

2011-10-17 16:44:40

原文地址:linux spi子系统 作者:chumojing

spi_master/spi_device和spi_driver的关系。

 

重要的数据结构:

~~~~~~~~~~
spi控制器的主题是spi_master,虽然一般不需要自己编写spi控制器驱动,了解这个结构体还是必要的。

  1. struct spi_master {  
  2.  struct device dev;  
  3.   
  4.  s16   bus_num;//总线编号,从零开始  
  5.   
  6.  u16   num_chipselect;//支持的片选的数量。从设备的片选号不能大于这个数  
  7.   
  8.  /* setup mode and clock, etc (spi driver may call many times) */  
  9.  int   (*setup)(struct spi_device *spi);//根据spi设备更新硬件配置。   
  10.   
  11.  int   (*transfer)(struct spi_device *spi, struct spi_message *mesg);//添加消息到队列的方法。这个函数不可睡眠。它的职责是安排发生的传送并且调用注册的回调函数complete()。  
  12.   
  13.  /* called on release() to free memory provided by spi_master */  
  14.  void   (*cleanup)(struct spi_device *spi);//cleanup函数会在spidev_release函数中被调用,spidev_release被登记为spi dev的release函数。  
  15. };  

spi控制器的驱动一般在arch/.../mach-*/board-*.c 声明,注册一个平台设备,然后在driver/spi下面建立一个平台驱动。
spi_master注册过程中会扫描arch/.../mach-*/board-*.c 中调用spi_register_board_info注册的信息,为每一个与本总线编号相同的信息建立一个spi_device。
根据Linux内核的驱动模型,注册在同一总线下的驱动和设备会进行匹配。spi_bus_type总线匹配的依据是名字。这样当自己编写的spi_driver和spi_device同名的时候,
spi_driver的probe方法就会被调用。spi_driver就能看到与自己匹配的spi_device了。

 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  1. struct spi_driver {  
  2.  int   (*probe)(struct spi_device *spi);//和spi匹配成功之后会调用这个方法。因此这个方法需要对设备和私有数据进行初始化。  
  3.  int   (*remove)(struct spi_device *spi);//解除spi_device和spi_driver的绑定,释放probe申请的资源。  
  4.  void   (*shutdown)(struct spi_device *spi);//关闭  
  5.  int   (*suspend)(struct spi_device *spi, pm_message_t mesg);//挂起  
  6.  int   (*resume)(struct spi_device *spi);//恢复  
  7.  struct device_driver driver;  
  8. };  

这个结构体是一个辅助性的数据结构。主要是提供驱动模型下的绑定方法和电源管理接口。其成员driver.name是和spi_device匹配的依据。
这个结构的用处是绑定arch/.../mach-*/board-*.c 中调用spi_register_board_info注册的信息对应的spi_device。
内核中的一个例子如下。

 

  1. static struct spi_driver tle62x0_driver = {  
  2.  .driver = {  
  3.   .name = "tle62x0",  
  4.   .owner = THIS_MODULE,  
  5.  },  
  6.  .probe  = tle62x0_probe,  
  7.  .remove  = __devexit_p(tle62x0_remove),  
  8. };  
  9.   
  10. static __init int tle62x0_init(void)  
  11. {  
  12.  return spi_register_driver(&tle62x0_driver);  
  13. }  
  14.   
  15. static __exit void tle62x0_exit(void)  
  16. {  
  17.  spi_unregister_driver(&tle62x0_driver);  
  18. }  

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  1. struct spi_device {  
  2.  struct device  dev;  
  3.  struct spi_master *master;//对应的控制器指针  
  4.  u32   max_speed_hz;//spi通信时钟  
  5.  u8   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) */  
  10. #define SPI_MODE_1 (0|SPI_CPHA)  
  11. #define SPI_MODE_2 (SPI_CPOL|0)  
  12. #define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)  
  13. #define SPI_CS_HIGH 0x04   /* chipselect active high? */片选电位为高  
  14. #define SPI_LSB_FIRST 0x08   /* per-word bits-on-wire */先输出低比特  
  15. #define SPI_3WIRE 0x10   /* SI/SO signals shared */输入输出共享接口,此时只能做半双工。  
  16. #define SPI_LOOP 0x20   /* loopback mode */回写/回显模式  
  17.  u8   bits_per_word;//每个字长的比特数。  
  18.  int   irq;//使用到的中断  
  19.  void   *controller_state;  
  20.  void   *controller_data;  
  21.  char   modalias[32];//名字。  
  22. };  
  23.   
  24. ~~~~~~~~~~~~~~~~~~~~~~~  
  25. 这个结构体描述设备的信息。  
  26. struct spi_board_info {  
  27.  char  modalias[32];//设备名  
  28.  const void *platform_data;//平台数据  
  29.  void  *controller_data;  
  30.  int  irq;//中断  
  31.   
  32.  /* slower signaling on noisy or low voltage boards */  
  33.  u32  max_speed_hz;//通信时钟  
  34.   
  35.  u16  bus_num;//总线号  
  36.  u16  chip_select;//片选号  
  37.   
  38.  u8  mode;//参考spi_device中的成员  
  39. };  

 

 

如何编写一个spi驱动?(设备驱动,这里不涉及spi控制器的驱动)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
静态声明
======
 声明从设备
 -----------------
 arch/.../mach-*/board-*.c中一般包含如下的声明
 

  1. static struct ads7846_platform_data ads_info = {  
  2.   .vref_delay_usecs = 100,  
  3.   .x_plate_ohms  = 580,  
  4.   .y_plate_ohms  = 410,  
  5.  };  
  6.   
  7.  static struct spi_board_info spi_board_info[] __initdata = {  
  8.  {  
  9.   .modalias = "ads7846",  
  10.   .platform_data = &ads_info,  
  11.   .mode  = SPI_MODE_0,  
  12.   .irq  = GPIO_IRQ(31),  
  13.   .max_speed_hz = 120000 /* max sample rate at 3V */ * 16,  
  14.   .bus_num = 1,  
  15.   .chip_select = 0,  
  16.  },  
  17.  };  

 使用以下函数注册上面声明的信息(注册进spi子系统):
 spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
 这种静态的注册,不需要注销。
 编写驱动
 -------------
 SPI通信驱动和平台设备驱动非常类似

  1. static struct spi_driver CHIP_driver = {  
  2.  .driver = {  
  3.   .name  = "CHIP",  
  4.   .owner  = THIS_MODULE,  
  5.  },  
  6.   
  7.  .probe  = CHIP_probe,  
  8.  .remove  = __devexit_p(CHIP_remove),  
  9.  .suspend = CHIP_suspend,  
  10.  .resume  = CHIP_resume,  
  11. };  

 驱动核心会将board_info的modalias成员为“CHIP”的设备和本驱动进行绑定。
 probe的代码应该像下面这样

  1. static int __devinit CHIP_probe(struct spi_device *spi)  
  2. {  
  3.  struct CHIP   *chip;  
  4.  struct CHIP_platform_data *pdata;  
  5.   
  6.  /* assuming the driver requires board-specific data: */  
  7.  pdata = &spi->dev.platform_data;  
  8.  if (!pdata)  
  9.   return -ENODEV;  
  10.   
  11.  /* get memory for driver's per-chip state */  
  12.  chip = kzalloc(sizeof *chip, GFP_KERNEL);//为自己的结构体申请内存空间。  
  13.  if (!chip)  
  14.   return -ENOMEM;  
  15.  spi_set_drvdata(spi, chip);  
  16.   
  17.  ... etc//其他句子。  
  18.  return 0;  
  19. }  

 probe做私有数据的初始化。之后就可以使用probe得到的spi_device和设备进行通信了。通信的过程根本不需要spi_driver。
 spi_driver这个结构体仅仅是为了让驱动得到相应spi_device的指针和进行电源管理;
 再在spi_driver之上使用input子系统或者混杂设备已经和spi本身无关了。
 spi_device是驱动的下界,它的上界包含了sysfs、输入子系统、ALSA、网络层、MTD、字符设备框架或者其他Linux子系统。

非静态声明
======
静态声明是在内核编译时便已经能确定spi设备了。但是有时无法在编译内核时确定,加入设备时谁都不想修改内核然后再重新编译。
上文我们说到,spi通信过程根本不需要spi_driver,因此仅仅建立我们的spi_device即可。
spi_busnum_to_master()可以根据总线号返回spi_master指针。原型如下:
struct spi_master *spi_busnum_to_master(u16 bus_num);
首先根据要使用的控制器编号(这个编号对应硬件的哪个控制器跟控制器驱动有关)获得spi_master指针,然后像静态声明时一样定义spi_board_info,
最后调用struct spi_device *spi_new_device(struct spi_master *master, struct spi_board_info *chip)申请并注册设备。返回非空指针意味着可以使用
返回的spi_device指针进行通信了。注意:从代码中看到,spi_new_device并不能初始化spi_device的bits_per_word成员,因此在得到spi_device之后还因该定义这个字长。
设备移走之后注销设备使用spi_unregister_device()。

 

如何通信
~~~~
 使用封装接口
==================
static inline int
spi_write(struct spi_device *spi, const u8 *buf, size_t len);//同步写,可能睡眠。成功返回零,失败返回负的值。
static inline int
spi_read(struct spi_device *spi, u8 *buf, size_t len)//同步读,写,可能睡眠。成功返回零,失败返回负的值。
extern int spi_write_then_read(struct spi_device *spi, const u8 *txbuf, unsigned n_tx,  u8 *rxbuf, unsigned n_rx);//可能睡眠。
 先写n_tx个字节再读n_rx个字节。成功返回零,失败返回负的值。读和写的字节数不应该大于32。
static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd)//上个函数的封装。写8bit然后再读8bit。
static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd)//写写8bit然后再读16bit。

 原始接口
================
static inline int
spi_async(struct spi_device *spi, struct spi_message *message);//异步读写。可以在不能睡眠的上下文调用。
extern int spi_sync(struct spi_device *spi, struct spi_message *message);//同步读写。会睡眠,不能在中断上下文中使用。所有同步的spi传输接口都是对它的封装。
下面研究spi_message 。

spi_message
~~~~~~~

  1. struct spi_message {  
  2.  struct list_head transfers;//此次消息的传输段队列。一个消息可以包含多个传输段。  
  3.   
  4.  struct spi_device *spi;//传输的目的设备  
  5.   
  6.  unsigned  is_dma_mapped:1;//如果为真,此次调用提供dma和cpu虚拟地址。  
  7.   
  8.  /* completion is reported through a callback */  
  9.  void   (*complete)(void *context);//异步调用完成后的回调函数  
  10.  void   *context;//回调函数的参数  
  11.  unsigned  actual_length;//此次传输的实际长度  
  12.  int   status;//执行的结果。成功被置0,否则是一个负的错误码。  
  13.   
  14.  struct list_head queue;//下面两个成员是给拥有本消息的驱动选用的。spi_master会使用它们。自己最好不要使用。  
  15.  void   *state;  
  16. };  

spi_message用来原子的执行spi_transfer表示的一串数组传输请求。
这个传输队列是原子的,这意味着在这个消息完成之前不会有其它消息占用总线。
消息的执行总是按照FIFO的顺序。
向底层提交spi_message的代码要负责管理它的内存空间。未显示初始化的内存需要使用0来初始化。
 
spi_transfer
----------------
上面看到,一个spi_message是由多个spi_transfer组成的。
每个spi_transfer总是读取和写入同样长度的比特数,但是可以很容易的使用空指针舍弃读或写。
为spi_transfer和spi_message分配的内存应该在消息处理期间保证是完整的。

  1. struct spi_transfer {  
  2.  /* it's ok if tx_buf == rx_buf (right?) 
  3.   * for MicroWire, one buffer must be null 
  4.   * buffers must work with dma_*map_single() calls, unless 
  5.   *   spi_message.is_dma_mapped reports a pre-existing mapping 
  6.   */  
  7.  const void *tx_buf;//要写入设备的数据(必须是dma_safe),或者为NULL.  
  8.  void  *rx_buf;//要读取的数据缓冲(必须是dma_safe),或者为NULL.  
  9.  unsigned len;//tx和rx的大小(字节数)。这里不是指它的和,而是各自的长度,他们总是相等的。  
  10.   
  11.  dma_addr_t tx_dma;//如果spi_message.is_dma_mapped是真,这个是tx的dma地址  
  12.  dma_addr_t rx_dma;//如果spi_message.is_dma_mapped是真,这个是rx的dma地址  
  13.   
  14.  unsigned cs_change:1;//影响此次传输之后的片选。指示本次transfer结束之后是否要重新片选并调用setup改变设置。这个标志可以减少系统开销。  
  15.  u8  bits_per_word;//每个字长的比特数。如果是0,使用默认值  
  16.  u16  delay_usecs;//此次传输结束和片选改变之间的延时,之后就会启动另一个传输或者结束整个消息。  
  17.  u32  speed_hz;//通信时钟。如果是0,使用默认值  
  18.   
  19.  struct list_head transfer_list;//用来连接的双向链表节点。  
  20. };   

控制器驱动会先写入tx的数据,然后读取同样长度的数据。长度指示是len。
如果tx_buff是空指针,填充rx_buff的时候会输出0(为了产生接收的时钟),如果rx_buff是NULL,接收到的数据将被丢弃。
只有len长读的数据会被输出和接收。
输出不完整的字长是错误的(比如字长为2字节的时候输出三个字节,最后一个字节凑不成一个整字)。

本地内存中的数据总是使用本地cpu的字节序,无论spi的字节序是大段模式还是小段模式(使用SPI_LSB_FIRS)
 当spi_transfer的字长不是8bit的2次幂的整数倍,这些数据字就包含扩展位。在spi通信驱动看来内存中的数据总是刚好
对齐的,所以rx中位定义和rx中未使用的比特位总是最高有效位。(比如13bit的字长,每个字占2字节,rx和tx都应该如此存放)            

所有的spi传输都以使能相关的片选线为开始。一般来说片选线在本消息结束之前保持有效的状态。驱动可以使用
spi_transfer中的cs_change成员来影响片选:
(i)如果transfer不是message的最后一个,这个标志量可以方便的将片选线置位无效的状态。
有时需要这种方法来告知芯片一个命令的结束并使芯片完成这一批处理任务。
(ii)当这个trasfer是最后一个时,片选可以一直保持有效知道下一个transfer到来。
在多spi从机的总线上没有办法阻止其他设备接收数据,这种方法可以作为一个特别的提示;开始往另一个设备传输信息就要先将
本芯片的片选置为无效。但在其他情况下,这可以保证正确性。一些设备后面的信息依赖于前面的信息并且在一个处理序列完成后需要
禁用片选线。
上面这段是翻译的,讲的不明白。
再说一下:cs_change影响此transfer完成后是否禁用片选线并调用setup改变配置。(这个标志量就是chip select change片选改变的意思)
没有特殊情况,一个spi_message因该只在最后一个transfer置位该标志量。

 

 

 

 

 

spi_message的函数接口
-------------------------------
static inline void spi_message_init(struct spi_message *m);//初始化,实际是用0填充并初始化链表节点。
static inline void
spi_message_add_tail(struct spi_transfer *t, struct spi_message *m);//将transfer加到message的链表尾部。
static inline void
spi_transfer_del(struct spi_transfer *t);//将transfer从message链表中移除
static inline struct spi_message *spi_message_alloc(unsigned ntrans, gfp_t flags);//创建一个message并创建 ntrans个transfer加入链表,flag是申请内存时的标志。
static inline void spi_message_free(struct spi_message *m);//释放message。

bits_per_word
-----------------------
在spi_device和spi_transfer中均有bits_per_word的定义。
spi控制器驱动中会首先使用spi_transfer中的定义,如果它的值为0则使用默认值,也就是spi_device的bits_per_word定义,如果它也为零,则使用默认值8(bit).

在spi_board_info 中没有定义bits_per_word,而且spi_write/spi_read等封装接口函数都没有初始化这个变量。因此如果字长为8不能满足需要,应该显式的初始化spi_device的bits_per_word,或者使用
原始接口并初始化spi_transfer的bits_per_word成员。
阅读(404) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~