spi总线 上层调用_Linux驱动分析之SPI驱动架构

本文介绍了Linux系统中SPI驱动的体系结构,包括SPI核心、控制器驱动和设备驱动三个部分,并详细讲解了spi_master、spi_driver、spi_device等重要结构体之间的关系。同时,阐述了spi_message和spi_transfer在数据传输过程中的作用,以及spi_async和spi_sync两个API函数的使用注意事项。
摘要由CSDN通过智能技术生成

SPI体系结构

主要由三部分组成:

(1) SPI核心

(2) SPI控制器驱动

(3) SPI设备驱动

基本和I2C的架构差不多

重要结构体

内核版本:3.7.6

  • spi_master
//SPI控制器
struct spi_master {
  struct device  dev;

  struct list_head list; //控制器链表

   //控制器对应的SPI总线号 SPI-2 对应bus_num= 2
  s16      bus_num;
  u16      num_chipselect;//控制器支持的片选数量,即能支持多少个spi设备 
  u16      dma_alignment;//DMA缓冲区对齐方式
  u16      mode_bits;// mode标志

  /* other constraints relevant to this driver */
  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;

  /* flag indicating that the SPI bus is locked for exclusive use */
  bool      bus_lock_flag;

    //设置SPI mode和时钟, 在spi_add_device中调用
  int      (*setup)(struct spi_device *spi);
    //传输数据函数, 实现数据的双向传输
  int      (*transfer)(struct spi_device *spi,
            struct spi_message *mesg);
  //注销时回调
  void      (*cleanup)(struct spi_device *spi);

  /*
   * These hooks are for drivers that want to use the generic
   * master transfer queueing mechanism. If these are used, the
   * transfer() function above must NOT be specified by the driver.
   * Over time we expect SPI drivers to be phased over to this API.
   */
  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);
}
  • spi_driver
//SPI驱动,和platform_driver,i2c_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);
  int  (*suspend)(struct spi_device *spi, pm_message_t mesg);
  int  (*resume)(struct spi_device *spi);
  struct device_driver  driver;
};
  • spi_device
//SPI 设备
struct spi_device {
  struct device    dev;
  struct spi_master  *master; //指向SPI控制器
  u32      max_speed_hz; //最大速率
  u8      chip_select; //片选
  u8      mode; //SPI设备模式,使用下面的宏
#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_message
//SPI传输数据结构体
struct spi_message {
  struct list_head  transfers; // spi_transfer链表头

  struct spi_device  *spi; //spi设备

  unsigned    is_dma_mapped:1;

  //发送完成回调
  void      (*complete)(void *context);
  void      *context;
  unsigned    actual_length;
  int      status;

  /* for optional use by whatever driver currently owns the
   * spi_message ...  between calls to spi_async and then later
   * complete(), that's the spi_master controller driver.
   */
  struct list_head  queue;
  void      *state;
};
  • spi_transfer
// 该结构体是spi_message下的子单元,
struct spi_transfer {
    
  const void  *tx_buf;// 发送的数据缓存区
  void    *rx_buf;// 接收的数据缓存区
  unsigned  len;

  dma_addr_t  tx_dma; //tx_buf的DMA地址
  dma_addr_t  rx_dma; //rx_buf的DMA地址

  unsigned  cs_change:1;
  u8    bits_per_word;
  u16    delay_usecs;
  u32    speed_hz;

  struct list_head transfer_list;
};

总结上面结构体关系:

1. spi_driver和spi_device

spi_driver对应一套驱动方法,包含probe,remove等方法。spi_device对应真实的物理设备,每个spi设备都需要一个spi_device来描述。spi_driver与spi_device是一对多的关系,一个spi_driver上可以支持多个同类型的spi_device。

2. spi_master和spi_device

spi_master 与 spi_device 的关系和硬件上控制器与设备的关系一致,即spi_device依附于spi_master。

3. spi_message和spi_transfer

spi传输数据是以 spi_message 为单位的,我们需要传输的内容在 spi_transfer 中。spi_transfer是spi_message的子单元。

1 . 将本次需要传输的 spi_transfer 以 spi_transfer->transfer_list 为链表项,连接成一个transfer_list链表,挂接在本次传输的spi_message spi_message->transfers链表下。

2 . 将所有等待传输的 spi_message 以 spi_message->queue 为链表项,连接成个链表挂接在queue下。

d625f8e4b84c3b0825fc1a473717ecbb.png

API函数

//分配一个spi_master
struct spi_master *spi_alloc_master(struct device *dev, unsigned size)

//注册和注销spi_master
int spi_register_master(struct spi_master *master)
void spi_unregister_master(struct spi_master *master)

//注册和注销spi_driver
int spi_register_driver(struct spi_driver *sdrv)
void spi_unregister_driver(struct spi_driver *sdrv)

//初始化spi_message
void spi_message_init(struct spi_message *m)
//向spi_message添加transfers
void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
//异步发送spi_message
int spi_async(struct spi_device *spi, struct spi_message *message)
//同步发送spi_message
int spi_sync(struct spi_device *spi, struct spi_message *message)

//spi同步写(封装了上面的函数)
int spi_write(struct spi_device *spi, const void *buf, size_t len)
//spi同步读(封装了上面的函数)
int spi_read(struct spi_device *spi, void *buf, size_t len)
//同步写并读取(封装了上面的函数)
int spi_write_then_read(struct spi_device *spi,
    const void *txbuf, unsigned n_tx,
    void *rxbuf, unsigned n_rx)

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

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

上面的传输函数最终都是调用spi_master的transfer()函数。

9f518a8a3417ac5b5c07c6f6d4010f34.png
更多精彩好文,关注微信公众号
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值