Linux驱动分析之SPI驱动架构

Posted 嵌入式软件开发交流

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux驱动分析之SPI驱动架构相关的知识,希望对你有一定的参考价值。


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下。


API函数




//分配一个spi_masterstruct spi_master *spi_alloc_master(struct device *dev, unsigned size)
//注册和注销spi_masterint spi_register_master(struct spi_master *master)void spi_unregister_master(struct spi_master *master)
//注册和注销spi_driverint spi_register_driver(struct spi_driver *sdrv)void spi_unregister_driver(struct spi_driver *sdrv)
//初始化spi_messagevoid spi_message_init(struct spi_message *m)//向spi_message添加transfersvoid spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)//异步发送spi_messageint spi_async(struct spi_device *spi, struct spi_message *message)//同步发送spi_messageint 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()函数。


总结



    

    SPI的架构和之前的I2C的结构基本差不多,我们会发现其实驱动中大量的结构体都是对参数和数据的封装。站在宏观的角度看,就是填充结构体,调用函数注册或发送。

    上面是对Linux中SPI相关架构的分析,后面依然会拿出一些相对应的驱动来进行具体分析。希望能做到理论和实践相结合!


> > > >

精品文章







长按识别图中二维码关注

后台回复【微信】可以加我,进技术交流群



以上是关于Linux驱动分析之SPI驱动架构的主要内容,如果未能解决你的问题,请参考以下文章

Linux驱动分析之SPI控制器

Linux驱动修炼之道-SPI驱动框架源码分析(上)转

Linux驱动分析之SPI设备

Linux SPI总线和设备驱动架构之三:SPI控制器驱动

Linux SPI总线和设备驱动架构之二:SPI通用接口层

Linux SPI总线和设备驱动架构之四:SPI数据传输的队列化