zoukankan      html  css  js  c++  java
  • 驱动_spi驱动框架

    spi驱动


     Spi总线上可以挂MMC、SD,flash、wifi网卡等常见设备。

    应用


    (从设备驱动层)

    spi_driver层

     (自己编写)


     (核心层)

               

    spi_core层                                 spi_bus_type

            

     (spi_core.c)


      (控制器层)

    spi_master层                                      

     (spi_xxx.c)


     硬件

    目录:linux/driver/spi/ 

    <从设备驱动层>  spidev.c                           自己编写

    <核      心     层>  spi.c                              内核提供

    <控   制   器  层>    spi-xxx.c(瑞芯微:spi-rockchip.c  全志:spi-sunxi.c)      原厂提供

     Makefile:

    <结构体>


    struct spi_transfer是对一次完整的数据传输的描述.每个spi_transfer总是读取和写入同样长度的比特数,但是可以很容易的使用空指针舍弃读或写.为spi_transfer和spi_message分配的内存应该在消息处理期间保证是完整的.

    struct spi_transfer {

      const void *tx_buf;

      /*发送缓冲区地址,这里存放要写入设备的数据(必须是dma_safe),或者为NULL*/

      void *rx_buf;

      /*接收缓冲区地址,从设备中读取的数据(必须是dma_safe)就放在这里,或者为NULL*/

      unsigned len;

      /*传输数据的长度.记录了tx和rx的大小(字节数),这里不是指它的和,而是各自的长度,他们总是相等的*/

      dma_addr_t tx_dma;

      /*如果spi_message.is_dma_mapped是真,这个是tx的dma地址*/

      dma_addr_t rx_dma;

      /*如果spi_message.is_dma_mapped是真,这个是rx的dma地址*/

      unsigned cs_change:1;

      /*影响此次传输之后的片选.指示本次transfer结束之后是否要重新片选并调用setup改变设置.若为1则表示当该transfer
        传输完后,改变片选信号.这个标志可以减少系统开销*/

      u8 bits_per_word;

      /*每个字长的比特数.如果是0,使用默认值*/

      u16 delay_usecs;

      /*此次传输结束和片选改变之间的延时,之后就会启动另一个传输或者结束整个消息*/

      u32 speed_hz;

      /*通信时钟.如果是0,使用默认值*/

      struct list_head transfer_list;

      /*用来连接的双向链表节点,用于将该transfer链入message*/
    };

    struct spi_message:就是对多个spi_transfer的封装.spi_message用来原子的执行spi_transfer表示的一串数组传输请求.这个传输队列是原子的,这意味着在这个消息完成之前不会有其它消息占用总线.消息的执行总是按照FIFO的顺序.向底层提交spi_message的代码要负责管理它的内存空间.未显示初始化的内存需要使用0来初始化.为spi_transfer和spi_message分配的内存应该在消息处理期间保证是完整的。

    struct spi_message {

      struct list_head transfers; 

     /*此次消息的传输段(spi_transfer)队列,一个消息可以包含多个传输段*/

      struct spi_device *spi;

     /*传输的目的设备,无论如何这里都是spi从设备,至于数据流向(是从主机到从设备还是从从设备到主机)这是由write/read
      每个传输段(spi_transfer)内部的tx_buf或者是rx_buf决定的*/

      unsigned is_dma_mapped:1;

     /*如果为真,此次调用提供dma和cpu虚拟地址.spi主机提供了dma缓存池.如果此消息确定要使用dma(那当然更好
      了).则从那个缓存池中申请高速缓存.替代传输段(spi_transfer)中的tx_buf/rx_buf*/

      void (*complete)(void*context);

     /*用于异步传输完成时调用的回调函数*/

      void *context; 

     /*回调函数的参数*/

      unsigned actual_length;

     /*此次传输的实际长度,这个长度包括了此消息spi_message中所有传输段spi_transfer传输的长度之和(不管每个传
      输段spi_transfer到底是输入还是输出,因为本来具体的传输就是针对每一个传输段spi_transfer来进行的)*/

      int status;

     /*执行的结果.成功被置0,否则是一个负的错误码*/

      struct list_head queue;

     /*用于将该message链入bitbang等待队列*/

      void *state;

    };

    struct spi_driver:用于描述SPI(从)设备驱动.驱动核心将根据driver.name和spi_board_info的modalias进行匹配,如过modalia和name相等,则绑定驱动程序和arch/.../mach-xxx/board-xxx.c中调用spi_register_board_info注册的信息对应的spi_device设备.它的形式和struct platform_driver是一致的. 

    struct spi_driver {
      const struct spi_device_id *id_table;

      int (*probe)(struct spi_device *spi);

      /*和spi_device匹配成功之后会调用这个方法.因此这个方法需要对设备和私有数据进行初始化*/

      int (*remove)(struct spi_device *spi);

      /*解除spi_device和spi_driver的绑定,释放probe申请的资源*/

      void (*shutdown)(struct spi_device *spi);

      /*一般牵扯到电源管理会用到,关闭*/

      int (*suspend)(struct spi_device *spi, pm_message_t mesg);

      /*一般牵扯到电源管理会用到,挂起*/

      int (*resume)(struct spi_device *spi);

      /*一般牵扯到电源管理会用到,恢复*/

      struct device_driver driver;
    };

    struct spi_device用来描述一个SPI从设备信息

    SPI子系统只支持主模式,也就是说SOC上的SPI只能工作在master模式,外围设备只能为slave模式
    struct spi_device {
      struct device dev;
      struct spi_master *master;  //对应的控制器指针
      u32 max_speed_hz;      //spi传输时钟
      u8 chip_select;    //片选号,用来区分同一主控制器上的设备
      u8 mode;  //各bit的定义如下,主要是传输模式/片选极性
      #define SPI_CPHA 0x01   /* clock phase */
      #define SPI_CPOL 0x02   /* clock polarity */
      #define SPI_MODE_0 (0|0)   /* (original MicroWire) */
      #define SPI_MODE_1 (0|SPI_CPHA)
      #define SPI_MODE_2 (SPI_CPOL|0)
      #define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
      #define SPI_CS_HIGH 0x04    /* chipselect active high? *//*片选电位为高*/
      #define SPI_LSB_FIRST 0x08    /* per-word bits-on-wire *//*先输出低比特*/
      #define SPI_3WIRE 0x10   /* SI/SO signals shared *//*输入输出共享接口,此时只能做半双工*/
      #define SPI_LOOP 0x20   /* loopback mode *//*回写/回显模式*/
      #define SPI_NO_CS 0x40    /* 1 dev/bus, no chipselect */
      #define SPI_READY 0x80    /* slave pulls low to pause */
      u8 bits_per_word;  /*每个字长的比特数*/
      int irq;       /*使用到的中断号*/
      void *controller_state;
      void *controller_data;
      char modalias[SPI_NAME_SIZE];  /*spi设备的名字*/
      int cs_gpio;    /* chip select gpio */

    };

    struct spi_board_info是板级信息,是在移植时就写好的,并且要将其注册.

    该结构也是对SPI(从)设备(spi_device)的描述,只不过它是板级信息,最终该结构的所有字段都将用于初始化SPI设备结构体spi_device

    struct spi_board_info {

      char modalias[SPI_NAME_SIZE];

      /*spi设备名,会拷贝到spi_device的相应字段中.这是设备spi_device在SPI总线spi_bus_type上匹配驱动的唯一标识*/

      const void *platform_data;  /*平台数据*/

      void *controller_data;

      int irq;  /*中断号*/

      u32 max_speed_hz;/*SPI设备工作时的波特率*/

      u16 bus_num;

      /*该SPI(从)设备所在总线的总线号,就记录了所属的spi_master之中的bus_num编号.一个spi_master就对应一条总线*/

      u16 chip_select;/*片选号.该SPI(从)设备在该条SPI总线上的设备号的唯一标识*/

      u8 mode;/*参考spi_device中的成员*/

    };

    struct spi_bitbang结构用于控制实际的数据传输.

    struct spi_bitbang {
      struct workqueue_struct *workqueue;   /*工作队列*/

      struct work_struct work;

      spinlock_t lock;

      struct list_head queue;

      u8 busy;

      u8 use_dma;

      u8 flags;    /* extra spi->mode support */

      struct spi_master *master;  /*bitbang所属的master*/

      int (*setup_transfer)(struct spi_device *spi,struct spi_transfer *t);  

      /*用于设置设备传输时的时钟,字长等*/

      void (*chipselect)(struct spi_device *spi, int is_on);

      #define BITBANG_CS_ACTIVE  1   /* normally nCS, active low */

      #define BITBANG_CS_INACTIVE  0

      int (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t);

      /* txrx_word[SPI_MODE_*]() just looks like a shift register */

      u32 (*txrx_word[4])(struct spi_device *spi,unsigned nsecs,u32 word, u8 bits);

    };

    struct spi_master用来描述一个SPI主控制器,我们一般不需要自己编写spi控制器驱动. 

    结构体master代表一个SPI接口,或者叫一个SPI主机控制器,一个接口对应一条SPI总线,master->bus_num则记录了这个总线号
    struct spi_master {
      struct device dev;

      struct list_head list;

      s16 bus_num;

      /*总线编号,从零开始.系统会用这个值去和系统中board_list链表中加入的每一个boardinfo结构
      (每个boardinfo结构都是一个spi_board_info的集合,每一个spi_board_info都是对应一个SPI(从)设备的描述)中的每一个
        spi_board_info中的bus_num进行匹配,如果匹配上就说明这个spi_board_info描述的SPI(从)设备是链接在此总线上           的,因此就会调用spi_new_device去创建一个spi_device*/

      u16 num_chipselect;

      //支持的片选的数量.从设备的片选号不能大于这个数.该值当然不能为0,否则会注册失败

      u16 dma_alignment;

      u16 mode_bits;

      u16 flags;

      #define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */

      #define SPI_MASTER_NO_RX BIT(1) /* can't do buffer read */

      #define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */

      spinlock_t bus_lock_spinlock;

      struct mutex bus_lock_mutex;

      bool bus_lock_flag;

      int (*setup)(struct spi_device *spi);

      //根据spi设备更新硬件配置

      int (*transfer)(struct spi_device *spi,struct spi_message *mesg);

      /*添加消息到队列的方法.此函数不可睡眠,其作用只是安排需要的传送,并且在适当的时候(传
         送完成或者失败)调用spi_message中的complete方法,来将结果报告给用户*/

      void (*cleanup)(struct spi_device *spi);

      /*cleanup函数会在spidev_release函数中被调用,spidev_release被登记为spi dev的release
         函数*/

      bool queued;

      struct kthread_worker kworker;

      struct task_struct *kworker_task;

      struct kthread_work pump_messages;

      spinlock_t queue_lock;

      struct list_head queue

      struct spi_message *cur_msg;

      bool busy;

      bool running;

      bool rt;

      int (*prepare_transfer_hardware)(struct spi_master *master);

      int (*transfer_one_message)(struct spi_master *master,

      struct spi_message *mesg);

      int (*unprepare_transfer_hardware)(struct spi_master *master);

      int *cs_gpios;
    };

    解释: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了. 

    数据传输接口

    spidev_sync_write

    spidev_sync_read  

       ↓

    spi_transfer       →    spi_massage →    spi_sync

    <编写框架>


    <笔记>


    1. 从设备驱动层,也叫协议驱动层

    2. 

    博客:

    http://blog.chinaunix.net/uid-21501855-id-5211900.html

    Stay hungry, stay foolish 待续。。。
  • 相关阅读:
    JavaScript 知识
    Sleep,Hibernate and Hybrid
    CentOS 7 休眠系统
    centos7如何添加开机启动服务/脚本
    linux下执行.sh文件的方法和语法
    systemctl命令
    linux 更新yum源 改成阿里云源
    Supervisor进程管理&开机自启
    Django中的Request和Response
    ORACLE 触发器
  • 原文地址:https://www.cnblogs.com/panda-w/p/11111038.html
Copyright © 2011-2022 走看看