书史足自悦,安用勤与劬。这篇文章主要讲述Linux驱动分析之SPI驱动架构相关的知识,希望能为你提供帮助。
【Linux驱动分析之SPI驱动架构】
SPI体系结构主要由三部分组成:
(1) SPI核心
(2) SPI控制器驱动
(3) SPI设备驱动
基本和I2C的架构差不多
重要结构体内核版本:3.7.6
- spi_master
//SPI控制器
struct spi_master {
struct devicedev;
struct list_head list; //控制器链表
//控制器对应的SPI总线号 SPI-2 对应bus_num= 2
s16bus_num;
u16num_chipselect; //控制器支持的片选数量,即能支持多少个spi设备
u16dma_alignment; //DMA缓冲区对齐方式
u16mode_bits; // mode标志
/* other constraints relevant to this driver */
u16flags;
#define SPI_MASTER_HALF_DUPLEXBIT(0)/* cant do full duplex */
#define SPI_MASTER_NO_RXBIT(1)/* cant do buffer read */
#define SPI_MASTER_NO_TXBIT(2)/* cant do buffer write */
// 并发同步时使用
spinlock_tbus_lock_spinlock;
struct mutexbus_lock_mutex;
/* flag indicating that the SPI bus is locked for exclusive use */
boolbus_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.
*/
boolqueued;
struct kthread_workerkworker;
struct task_struct*kworker_task;
struct kthread_workpump_messages;
spinlock_tqueue_lock;
struct list_headqueue;
struct spi_message*cur_msg;
boolbusy;
boolrunning;
boolrt;
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_driverdriver;
};
- spi_device
//SPI 设备
struct spi_device {
struct devicedev;
struct spi_master*master; //指向SPI控制器
u32max_speed_hz; //最大速率
u8chip_select; //片选
u8mode; //SPI设备模式,使用下面的宏
#defineSPI_CPHA0x01/* clock phase */
#defineSPI_CPOL0x02/* clock polarity */
#defineSPI_MODE_0(0|0)/* (original MicroWire) */
#defineSPI_MODE_1(0|SPI_CPHA)
#defineSPI_MODE_2(SPI_CPOL|0)
#defineSPI_MODE_3(SPI_CPOL|SPI_CPHA)
#defineSPI_CS_HIGH0x04/* chipselect active high? */
#defineSPI_LSB_FIRST0x08/* per-word bits-on-wire */
#defineSPI_3WIRE0x10/* SI/SO signals shared */
#defineSPI_LOOP0x20/* loopback mode */
#defineSPI_NO_CS0x40/* 1 dev/bus, no chipselect */
#defineSPI_READY0x80/* slave pulls low to pause */
u8bits_per_word;
intirq;
void*controller_state; //控制器运行状态
void*controller_data; //特定板子为控制器定义的数据
charmodalias[SPI_NAME_SIZE];
};
- spi_message
//SPI传输数据结构体
struct spi_message {
struct list_headtransfers; // spi_transfer链表头
struct spi_device*spi; //spi设备
unsignedis_dma_mapped:1;
//发送完成回调
void(*complete)(void *context);
void*context;
unsignedactual_length;
intstatus;
/* for optional use by whatever driver currently owns the
* spi_message ...between calls to spi_async and then later
* complete(), thats the spi_master controller driver.
*/
struct list_headqueue;
void*state;
};
- spi_transfer
// 该结构体是spi_message下的子单元,
struct spi_transfer {
const void*tx_buf; // 发送的数据缓存区
void*rx_buf; // 接收的数据缓存区
unsignedlen;
dma_addr_ttx_dma; //tx_buf的DMA地址
dma_addr_trx_dma; //rx_buf的DMA地址
unsignedcs_change:1;
u8bits_per_word;
u16delay_usecs;
u32speed_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_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()函数。
总结SPI的架构和之前的I2C的结构基本差不多,我们会发现其实驱动中大量的结构体都是对参数和数据的封装。站在宏观的角度看,就是填充结构体,调用函数注册或发送。
上面是对Linux中SPI相关架构的分析,后面依然会拿出一些相对应的驱动来进行具体分析。希望能做到理论和实践相结合!

文章图片
推荐阅读
- DataX安装及基本使用
- printf缓冲区刷新问题
- HA-Spark集群环境搭建(Yarn模式)
- Spark-Local模式环境搭建
- go中使用type关键字来定义类型别名
- 覆盖WordPress核心函数require_wp_db()
- 通过插件覆盖WordPress主题页面
- 覆盖子主题中的模块
- .onscroll函数在单个帖子页面上不起作用