当前位置:   article > 正文

Linux内核设备驱动:SPI驱动_linux spi驱动

linux spi驱动

Linux主机驱动和外设驱动分离思想

SPI驱动总线架构:SPI核心层(x),SPI控制器驱动层(x),SPI设备驱动层(√)

2 Linux SPI驱动总体架构
在2.6的linux内核中,SPI的驱动架构可以分为如下三个层次:SPI 核心层、SPI控制器驱动层和SPI设备驱动层。
Linux 中SPI驱动代码位于drivers/spi目录。
2.1 SPI核心层
SPI核心层是Linux的SPI核心部分,提供了核心数据结构的定义、SPI控制器驱动和设备驱动的注册、注销管理等API。其为硬件平台无关层,向下屏蔽了物理总线控制器的差异,定义了统一的访问策略和接口;其向上提供了统一的接口,以便SPI设备驱动通过总线控制器进行数据收发。
Linux中,SPI核心层的代码位于driver/spi/ spi.c。由于该层是平台无关层,本文将不再叙述,有兴趣可以查阅相关资料。
2.2 SPI控制器驱动层
SPI控制器驱动层,每种处理器平台都有自己的控制器驱动,属于平台移植相关层。它的职责是为系统中每条SPI总线实现相应的读写方法。在物理上,每个SPI控制器可以连接若干个SPI从设备。
在系统开机时,SPI控制器驱动被首先装载。一个控制器驱动用于支持一条特定的SPI总线的读写。一个控制器驱动可以用数据结构struct spi_master来描述。

3.SPI相关的数据结构

  3.1 struct spi_master 用于描述一个SPI控制器

  1. //在include/liunx/spi/spi.h文件中,在数据结构struct spi_master定义如下:
  2. struct spi_master {
  3. struct device dev;
  4. s16 bus_num;
  5. u16 num_chipselect;
  6. int (*setup)(struct spi_device *spi);
  7. int (*transfer)(struct spi_device *spi, struct spi_message *mesg);
  8. void (*cleanup)(struct spi_device *spi);
  9. };

bus_num为该控制器对应的SPI总线号。
num_chipselect 控制器支持的片选数量,即能支持多少个spi设备
setup函数是设置SPI总线的模式,时钟等的初始化函数, 针对设备设置SPI的工作时钟及数据传输模式等。在spi_add_device函数中调用。
transfer函数是实现SPI总线读写方法的函数。实现数据的双向传输,可能会睡眠

  cleanup 注销的时候调用

  3.2  SPI设备驱动层

SPI设备驱动层为用户接口层,其为用户提供了通过SPI总线访问具体设备的接口。
SPI设备驱动层可以用两个模块来描述,struct spi_driver和struct spi_device。
相关的数据结构如下:

struct spi_driver 用来描述一个SPI设备的驱动信息

  1. struct spi_driver {
  2. int (*probe)(struct spi_device *spi);
  3. int (*remove)(struct spi_device *spi);
  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是为device服务的,spi_driver注册时会扫描SPI bus上的设备,进行驱动和设备的绑定,probe函数用于驱动和设备匹配时被调用。从上面的结构体注释中我们可以知道,SPI的通信是通过消息队列机制,而不是像I2C那样通过与从设备进行对话的方式。

struct spi_device 用来描述一个SPI总线上的从设备
通常来说spi_device对应着SPI总线上某个特定的slave。并且spi_device封装了一个spi_master结构体。
spi_device结构体包含了私有的特定的slave设备特性,包括它最大的频率,片选那个,输入输出模式等等

  1. struct spi_device {
  2. struct device dev;
  3. struct spi_master *master;
  4. u32 max_speed_hz;
  5. u8 chip_select;
  6. u8 mode;
  7. u8 bits_per_word;
  8. int irq;
  9. void *controller_state;
  10. void *controller_data;
  11. char modalias[32];
  12. };

4 spi_device以下一系列的操作是在platform板文件中完成!

spi_device的板信息用spi_board_info结构体来描述:

  spi_board_info参数

  1. .modalias = "rc522", //初始化设备的名称
  2. .platform_data = NULL,
  3. .max_speed_hz = 10*1000*1000, //初始化传输速率
  4. .bus_num = 2, //控制器编号
  5. .chip_select = 0, //控制器片选的编号
  6. .mode = SPI_MODE_0, //spi的模式 CPOL=0, CPHA=0 此处选择具体数据传输模式
  7. .controller_data = &spi2_csi[0], //片选IO的信息

spi2_board_info设备描述结构体,设备注册函数spi_register_board_info

而这个info在init函数调用的时候会初始化:

spi_register_board_info(s3c_spi_devs,ARRAY_SIZE(s3c_spi_devs));//注册spi_board_info。

这个代码会把spi_board_info注册到链表board_list上。

spi_device封装了一个spi_master结构体,事实上spi_master的注册会在spi_register_board_info之后,spi_master注册的过程中会调用scan_boardinfo扫描board_list,找到挂接在它上面的spi设备,然后创建并注册spi_device。

至此spi_device就构建并注册完成了!

5 spi_driver的构建与注册

driver有几个重要的结构体:spi_driver、spi_transfer、spi_message

driver有几个重要的函数 :spi_message_init、spi_message_add_tail、spi_sync

//spi_driver的构建

  1. static struct spi_driver m25p80_driver = {
  2. .driver = {
  3. .name ="m25p80",
  4. .bus =&spi_bus_type,
  5. .owner = THIS_MODULE,
  6. },
  7. .probe = m25p_probe,
  8. .remove =__devexit_p(m25p_remove),
  9. };

//spidriver的注册

spi_register_driver(&m25p80_driver);

在有匹配的spi_device时,会调用m25p_probe

probe里完成了spi_transfer、spi_message的构建;

spi_message_init、spi_message_add_tail、spi_sync、spi_write_then_read函数的调用

在SPI总线上是通过封装一系列的spi_transfer到一个spi_message中,然后将spi_message提交到SPI子系统去。

下面是spi_transfer结构:

  1. struct spi_transfer {
  2. const void*tx_buf; //驱动提供的发送缓冲区dma,
  3. void *rx_buf; //接收缓冲区
  4. unsigned len; //长度一般是8
  5. dma_addr_ttx_dma; //发送dma,controller使用
  6. dma_addr_t rx_dma; //接收dma
  7. unsigned cs_change:1; //片选位
  8. u8 bits_per_word; //每字长度
  9. u16 delay_usecs; //延迟
  10. u32 speed_hz; //速度
  11. struct list_headtransfer_list; //transfer 链表
  12. };

在spi_transfer中时常更改的域也许只有len,tx_buf和rx_buf。剩下的当以0来初始化。

单个spi_transfer可表示一次读,一次写或者是一次读写。在SPIcontroller驱动下,所有操作常是全双工的。向spi_transfer中rx_buf传递一个NULL,这就是一次只写操作,会丢弃MISO线上的数据。同样向tx_buf传递一个NULL,这就是一次只读操作了。spi_transfer中len域代表(已经多少字节数据流过总线了)howmany bytes to clock the bus。

spi_message结构:

  1. struct spi_message {
  2. struct list_head transfers;
  3. struct spi_device *spi;
  4. unsigned is_dma_mapped:1;
  5. void (*complete)(void*context);
  6. void *context;
  7. unsigned actual_length;
  8. int status;
  9. struct list_head queue;
  10. void *state;
  11. };

transfer这个spi_message所包含有的spi_transfer链表头。

is_dma_mappedspi_transfer中tx_dma和rx_dma是否已经mapped。

complete回调函数

context 提供给complete的可选参数

actual_lengthspi_message已经传输了的字节数

status 出错与否,错误时返回errorcode

queue 和state 供controller驱动内部使用

在每次使用spi_message可以使用函数void spi_message_init(structspi_message *m);来初始化。

向spi_message添加transfers可以使用spi_message_add_tail()函数:

void spi_message_add_tail(structspi_transfer *t, struct spi_message *m);

一旦你准备好了spi_message,就可以使用spi_async()来向SPI系统提交了:

int spi_async(struct spi_device *spi,struct spi_message *message);

因为是异步的,一提交就立马返回了,这也就是说需要同步机制(complete就是了)。他确保不会睡眠,可安全的在中断handler或其他不可休眠的代码中调用。稍后会念念他的好的。

使用spi_async()需要注意的是,在complete()未返回前不要轻易访问你一提交的spi_transfer中的buffer。也不能释放SPI系统正在使用的buffer。一旦你的complete返回了,这些buffer就又是你的了。

使用完成回调机制稍显复杂,可以使用SPI系统提供的另一个同步版本:spi_sync():

int spi_sync(struct spi_device *spi,struct spi_message *message);

因为是同步的,spi_sync提交完spi_message后不会立即返回,会一直等待其被处理。一旦返回就可以重新使用buffer了。spi_sync()在drivers/spi/spi.c中实现,其调用了spi_async(),并休眠直至complete返回。

  1. 1 #include <linux/init.h>
  2. 2 #include <linux/module.h>
  3. 3 #include <linux/ioctl.h>
  4. 4 #include <linux/fs.h>
  5. 5 #include <linux/device.h>
  6. 6 #include <linux/err.h>
  7. 7 #include <linux/list.h>
  8. 8 #include <linux/errno.h>
  9. 9 #include <linux/mutex.h>
  10. 10 #include <linux/slab.h>
  11. 11 #include <linux/compat.h>
  12. 12 #include <linux/spi/spi.h>
  13. 13 #include <linux/spi/spidev.h>
  14. 14 #include <asm/uaccess.h>
  15. 15 #include <linux/gpio.h>
  16. 16 #include <mach/gpio.h>
  17. 17 #include <plat/gpio-cfg.h>
  18. 18 #include <linux/delay.h>
  19. 19 #include <linux/miscdevice.h>
  20. 20
  21. 21 struct spi_device *my_spi;
  22. 22
  23. 23 #define RC522_RESET_PIN EXYNOS4_GPK1(0)
  24. 24
  25. 25 void my_rc522_reset() // 驱动初始化 (IO部分)
  26. 26 {
  27. 27 //printk("************************ %s\n", __FUNCTION__);
  28. 28 if(gpio_request_one(RC522_RESET_PIN, GPIOF_OUT_INIT_HIGH, "RC522_RESET"))
  29. 29 pr_err("failed to request GPK1_0 for RC522 reset control\n");
  30. 30
  31. 31 s3c_gpio_se
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/712363
推荐阅读
相关标签
  

闽ICP备14008679号