Linux的SPI子系统驱动框架简析

一、SPI子系统介绍

  1. SPI总线的四种模式,CPOL决定空闲时SCLK的电平,CPHA决定第几个时钟边沿采集数据,常用模式0和模式3,均是上升沿采集数据
  2. SPI存在一个主从模式的分别,CPU中常用的是主模式

二、SPI子系统的分层介绍

  1. SPI设备驱动,包含一般的SPI设备驱动和spidev.c,形成两种节点/dev/xxx和/dev/spiB.C
  2. SPI核心层,包含spi.c,提供了各种API供上下层使用
  3. SPI控制器驱动,包含SPI控制器的驱动,和使用gpio模拟SPI控制器的驱动

三、典型的设备树文件

// 挂在spi0下的spi设备,片选引脚0
// 对于spidev.c对应的设备,compatible属性应该参考源文件
&spi0{
  spi_dev@0{
    compatible = "spi_dev_compatible";
    reg = <0>;
    spi-max-frequency = <10000>;
    ...    // 其他的配置参考documentation
  };
};
// spi控制器
spi_adap_virt: virtual_spi_adapter {
  compatible = "user,virtual_spi_adapter";
  #address-cells = <1>;
  #size-cells = <0>;
  ...      // 其他信息
  ...      // 可能存在的pinctrl信息
};
// 使用GPIO模拟的SPI控制器
spix{
  compatible = "spi-gpio";
  #address-cells = <1>;
  #size-cells = <0>;
  sck-gpios = <&gpio 95 0>;
  miso-gpios = <&gpio 96 0>;
  mosi-gpios = <&gpio 97 0>;
  cs-gpios = <&gpio 125 0>;
  num-chipselects = <1>;      // 片选线的数目 
};

四、SPI的重要结构体

// 描述SPI控制器
#define spi_master spi_controller
struct spi_controller{
  struct device dev;          // 所有设备都继承自device  
  struct list_head list;      // 组织spi控制器的链表
  ...                         // spi控制器的其余属性
  struct list_head queue;     // 存储每个msg需要发送的queue
  struct spi_message *cur_msg;// 当前传输的message
  int (*transfer)(struct spi_device *spi, struct spi_message *mesg);    // 老方法
  iny (*transfer_one)(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *transfer);  // 新方法
};
// 描述SPI传输的最基本单位
struct spi_transfer{
  const void *tx_buf;
  void       *rx_buf;
  unsigned    len;
  ...
};
// 管理SPI传输的单位,组织spi_transfer
struct spi_message{
  struct list_head transferes;  // 一组需要传输的spi_transfer
  struct spi_device *spi;       // 一个SPI设备
  void (*complete)(void*context);  // 完成量增加函数,提交完成量
  void *context;                // 函数参数,保存完成量
  unsigned actual_length;       // 传输长度,每次传输完成增加
  int status;                   // 传输完成0,等待工作队列传输-EINPROGESS
  struct list_head queue;       // 放入master的queue,用来遍历寻找spi_message
  ...
};
// 描述SPI驱动,类似platform_driver
struct spi_driver{        
  const struct spi_device_id *id_table;
  int (*probe)(struct spi_device *spi);
  int (*remove)(struct spi_device *spi);
  void (*shutdown)(struct spi_device *spi);
  struct device_driver driver;    // 父对象
};
// 描述SPI设备
struct spi_device{
  struct deie dev;      // 父对象
  struct spi_controller *master;  // 属于哪个控制器
  ...                    // 其他成员
};

五、驱动程序的流程

// SPI设备驱动
1. 入口函数中,使用spi_register_driver注册一个spi_driver,出口函数中spi_unregister_driver注销一个spi_driver
2. SPI总线和platform总线类似,compatible属性匹配时,调用probe函数
3. probe函数中,类似其他字符设备。使用alloc_chrdev_region、cdev_init、cdev_add、class_create、device_create
4. 关键的file_operation结构体,构建一个spi_transfer,并加入spi_message,调用spi_sync完成读写操作
// SPI控制器驱动
1. 使用平台设备驱动程序的框架,注册SPI控制器驱动
2. probe函数使用spi_alloc_master构建一个spi_master结构体,设置结构体的transfer函数,设置好工作队列和对应函数,使用spi_register_master注册一个spi_master结构体
3. remove函数中spi_register_master注销spi_master结构体
4. 关键的transfer函数,传输完成使用回调函数通知,或者启动工作队列,在工作队列中对控制器队列找到mesg的队列,传输完成使用回调函数通知
// SPI控制器的驱动-新
1. 使用平台设备驱动程序的框架,注册SPI控制器驱动
2. probe函数中使用spi_alloc_master构建一个spi_master结构体,并额外分配一个spi_bitbang结构体,设置bitbang结构体的txrx_bufs函数,初始化一个完成量,最后spi_bitbang_start启动
3. remove函数中,使用spi_bitbang_stop停止,使用spi_master_put释放spi_master结构体
4. 关键的txrx_bufs函数中,reinit_completion,等待ISR发送完成量,此处等待completion,最后返回transfer->len

六、spi传输函数的解析

// spi_sync老方法
spi_sync(struct spi_device *spi, struct spi_message *message);      // 实际的spi传输函数,传入参数spi_device指针、spi_message指针
  __spi_sync    // 传入参数spi_device指针、spi_message指针
    message->complete = spi_complete;      //完成量唤醒任务
    message->context = &done;              //完成量
    message->spi = spi;                    //对应的spi设备
    spi_async_locked(spi, message);
      __spi_async(spi, message);
        struct spi_controller *ctlr = spi->controller;    // 找到控制器
        ctlr->transfer(spi, message);      // 调用控制器提供的transfer函数
    wait_for_complete(&done);              //等待完成量,完成量在spi_complete被增加
spi控制器的transfer函数
  list_add_tail(&mesg->queue, &master->queue);    // 将msg的queue放入maser的queue
  schedule_work();                                // 启动工作队列
    mesg = list_entry(master->queue.next, struct spi_message, queue);  // 取出mesg
    list_del_init(&mesg->queue);                  // 移除当前mesg
    // 取出mesg的xfer,进行传输
    mesg->status = 0;
    if(mesg->complete)
      mesg->complete(mesg->context);        // 。回调函数,提交完成量
// spi_sync新方法
spi_sync(struct spi_device *spi, struct spi_message *message);      // 实际的spi传输函数,传入参数spi_device指针、spi_message指针
  __spi_sync    // 传入参数spi_device指针、spi_message指针
    struct spi_controller *ctlr = spi->controller;    // 找到控制器
    message->complete = spi_complete;      //完成量唤醒任务
    message->context = &done;              //完成量
    message->spi = spi;                    //对应的spi设备
    __spi_queued_transfer(spi, message, false);  //实现了master的transfer函数的第一步,将msg的queue放入maser的queue
    __spi_pump_message(ctrl, false);
      ctrl->cur_msg = list_first_entry(&ctrl->queue, struct spi_message, queue);      // 取出mesg
      list_del_init(&msg->queue);          // 移除mesg
      ctrl->transfer_one_message(ctrl, msg);  // 调用控制器提供的transfer_one_message函数
    wait_for_complete(&done);              //等待完成量,完成量在spi_complete被增加
spi控制器的transfer_one_message函数
posted @ 2025-08-05 15:45  gramming  阅读(373)  评论(0)    收藏  举报