SPI是“Serial Peripheral Interface”的缩写,串行外设接口,是一种四线制的同步串行通信接口,用来连接MCU、传感器、存储设备,SPI设备分为主设备和从设备两种,用于通信和控制的四根线分别是:CS(片选信号)、SCLK(时钟信号)、MISO(主设备的数据输入、从设备的数据输出脚)、MOSI(主设备的数据输出、从设备的数据输入脚)。
一、硬件结构
通常,负责发出时钟信号的设备为主设备,另一方为从设备。下图是一个SPI系统的硬件连接示意图:
主设备对应SoC芯片中的SPI控制器,通常,一个SoC中可能存在多个SPI控制器,如上图所示,SoC芯片中有3个SPI控制器。每个SPI控制器可以连接多个SPI从设备,每个从设备有各自独立的CS引脚。每个从设备共享另外三个信号引脚:SCLK、MISO、MOSI。任何时刻,只有一个CS引脚处于有效状态,与该有效CS引脚连接的设备此时可以与主设备(SPI控制器)通信,其它的从设备处于等待状态,并且它们的3个引脚必须处于高阻状态。
二、工作时序
按照时钟信号和数据信号之间的相位关系,SPI有四种工作时序模式:
用CPOL表示时钟信号的初始电平的状态,CPOL为0时表示时钟信号初始状态为低电平,为1时表示时钟信号初始状态为高电平。用CPHA表示在那个时钟沿采样数据,CPHA为0时表示在首个时钟变化沿采样数据,而CPHA为1时表示要在第二个时钟变化沿来采样数据。内核用CPOL和CPHA的组合来表示当前SPI需要的工作模式:
1、CPOL=0,CPHA=0 模式为0
2、CPOL=0,CPHA=1 模式为1
3、
CPOL=1,CPHA=0 模式为2
4、CPOL=1,CPHA=1 模式为3
三、确定驱动文件
SPI作为Linux的一个小子系统,驱动程序位于/drivers/spi/*目录,首先,可以通过Makefile和Kconfig来确定需要分析的源文件。
1、Makefile
#
# Makefile for kernel SPI drivers.
#
# small core, mostly translating board-specific
# config declarations into driver model code
obj-$(CONFIG_SPI_MASTER) += spi.o
obj-$(CONFIG_SPI_SPIDEV) += spidev.o
# SPI master controller drivers (bus)
obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o
obj-$(CONFIG_SPI_IMX) += spi-imx.o
2、对应的Kconfig配置内核
+---------------------------------------------------------------------------------------------+ |
| | --- SPI support | |
| | [ ] Debug support for SPI drivers | |
| | *** SPI Master Controller Drivers *** | |
| | Altera SPI Controller | |
| | {M} Utilities for Bitbanging SPI masters | |
| | Parallel port adapter for AVR Butterfly (DEVELOPMENT) | |
| | GPIO-based bitbanging SPI Master | |
| | Parallel port adapter for LM70 eval board (DEVELOPMENT) | |
| | OpenCores tiny SPI | |
| | PXA2xx SSP SPI master
3、编译生成的目标文件如下:
通过以上分析知道,SPI驱动由三部分组成,分别是core、master controller driver以及SPI protocol drivers。
四、数据结构分析
SPI驱动涉及的数据结构主要位于include/linux/spi/spi.h。
1、数据结构spi_master
spi_master代表一个主机控制器,一般不需要自己编写spi控制器驱动,但了解这个结构体还是必要的。
struct spi_master {
struct device dev; //设备模型使用
struct list_head list;
/* other than negative (== assign one dynamically), bus_num is fully
* board-specific. usually that simplifies to being SOC-specific.
* example: one SOC has three SPI controllers, numbered 0..2,
* and one board's schematics might show it using SPI-2. software
* would normally use bus_num=2 for that controller.
*/
s16 bus_num; // 总线(或控制器)编号
/* chipselects will be integral to many controllers; some others
* might use board-specific GPIOs.
*/
u16 num_chipselect; // 片选数量,决定该控制器下面挂接多少个SPI设备,从设备的片选号不能大于这个数量
/* some SPI controllers pose alignment requirements on DMAable
* buffers; let protocol drivers know about these requirements.
*/
u16 dma_alignment;
/* spi_device.mode flags understood by this controller driver */
u16 mode_bits; // master支持的设备模式
/* bitmask of supported bits_per_word for transfers */
u32 bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))
/* limits on transfer speed */
u32 min_speed_hz;
u32 max_speed_hz;
/* 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 */
#define SPI_MASTER_MUST_RX BIT(3) /* requires rx */
#define SPI_MASTER_MUST_TX BIT(4) /* requires tx */
/* lock and mutex for SPI bus locking */
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;
/* Setup mode and clock, etc (spi driver may call many times).
*
* IMPORTANT: this may be called when transfers to another
* device are active. DO NOT UPDATE SHARED REGISTERS in ways
* which could break those transfers.
*/
int (*setup)(struct spi_device *spi); // 根据SPI设备更新硬件配置。设置模式、时钟等,这个需要自己具体实现,主要设置SPI控制器和工作方式
/* bidirectional bulk transfers
*
* + The transfer() method may not sleep; its main role is
* just to add the message to the queue.
* + For now there's no remove-from-queue operation, or
* any other request management
* + To a given spi_device, message queueing is pure fifo
*
* + The master's main job is to process its message queue,
* selecting a chip then transferring data
* + If there are multiple spi_device children, the i/o queue
* arbitration algorithm is unspecified (round robin, fifo,
* priority, reservations, preemption, etc)
*
* + Chipselect stays active during the entire message
* (unless modified by spi_transfer.cs_change != 0).
* + The message transfers use clock and SPI mode parameters
* previously established by setup() for this device
*/
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg);// 添加消息到队列的方法。这个函数不可以睡眠。主要是安排发生的传送并且调用注册的回调函数complete()。这个不同的控制器要具体实现,传输数据最后都要调用这个函数
/* called on release() to free memory provided by spi_master */
void (*cleanup)(struct spi_device *spi);// 在spidev_release函数中被调用。
/*
* Used to enable core support for DMA handling, if can_dma()
* exists and returns true then the transfer will be mapped
* prior to transfer_one() being called. The driver should
* not modify or store xfer and dma_tx and dma_rx must be set
* while the device is prepared.
*/
bool (*can_dma)(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *xfer);
/*
* 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;
bool auto_runtime_pm;
bool cur_msg_prepared;
bool cur_msg_mapped;
struct completion xfer_completion;
size_t max_dma_len;
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 (*prepare_message)(struct spi_master *master,
struct spi_message *message);
int (*unprepare_message)(struct spi_master *master,
struct spi_message *message);
/*
* These hooks are for drivers that use a generic implementation
* of transfer_one_message() provied by the core.
*/
void (*set_cs)(struct spi_device *spi, bool enable);
int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
struct spi_transfer *transfer);
/* gpio chip select */
int *cs_gpios;
/* DMA channels for use with core dmaengine helpers */
struct dma_chan *dma_tx;
struct dma_chan *dma_rx;
/* dummy data for full duplex devices */
void *dummy_rx;
void *dummy_tx;
};
2、数据结构spi_device
spi_device代表一个外围spi设备,由主控制器驱动注册完成后扫描BSP中注册设备产生的设备链表并向spi_bus注册产生。在Linux内核中,每个spi_device代表一个物理的spi设备。
struct spi_device {
struct device dev;// 设备模型使用
struct spi_master *master;// 设备使用的master结构,挂接在哪个主控制器下
u32 max_speed_hz;// 通信时钟最大频率
u8 chip_select;// 片选号,每个master支持多个spi_device
u8 bits_per_word;// 每个字长的比特数,默认是8
u16 mode;// 设备支持的模式,如片选是高还是低
#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 */
#define SPI_TX_DUAL 0x100 /* transmit with 2 wires */
#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */
#define SPI_RX_DUAL 0x400 /* receive with 2 wires */
#define SPI_RX_QUAD 0x800 /* receive with 4 wires */
int irq;// 中断号
void *controller_state;// 控制寄存器状态
void *controller_data;
char modalias[SPI_NAME_SIZE];
int cs_gpio; /* chip select gpio */
/*
* likely need more hooks for more protocol options affecting how
* the controller talks to each chip, like:
* - memory packing (12 bit samples into low bits, others zeroed)
* - priority
* - drop chipselect after each word
* - chipselect delays
* - ...
*/
};
由于一个SPI总线上可以有多个SPI设备,需要片选号来区分它们,SPI控制器根据片选号来选择不同的片选线,从而实现每次只同一个设备通信。
spi_device的mode成员有两个比特位含义很重要。SPI_CPHA选择对数据线采样的时机,0选择每个时钟周期的第一个沿跳变时采样数据,1选择第二个时钟沿采样数据;SPI_CPOL选择每个时钟周期开始的极性,0表示时钟以低电平开始,1选择高电平开始。这两个比特位有四种组合,对应SPI_MODE_0~SPI_MODE_3。
另一个比较重要的成员是
bits_per_word。这个成员指定每次读写的字长,单位是比特。注意:如果这个成员为0,默认使用8作为字长。
3、数据结构spi_driver
spi_driver代表一个SPI协议驱动,也就是外设驱动。
struct spi_driver {
const struct spi_device_id *id_table; // 支持的spi_device设备表
int (*probe)(struct spi_device *spi);
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;// 设备模型使用
};
对于从事驱动开发的工程师来说,spi设备的驱动主要实现这个结构体的各个接口,将之注册到SPI子系统中去。
4、数据结构spi_transfer
spi_transfer代表一个读写缓冲对,包含接收缓冲区以及发送缓冲区,其实,spi_transfer的发送是通过构建spi_message实现,通过将spi_transfer中的链表transfer_list链接到spi_message中的transfers,再以spi_message形式向底层发送数据。每个spi_transfer都可以对传输的一些参数进行设置,使得主控制器按照它要求的参数进行数据发送。
struct spi_transfer {
/* it's ok if tx_buf == rx_buf (right?)
* for MicroWire, one buffer must be null
* buffers must work with dma_*map_single() calls, unless
* spi_message.is_dma_mapped reports a pre-existing mapping
*/
const void *tx_buf;
void *rx_buf;
unsigned len;
dma_addr_t tx_dma;
dma_addr_t rx_dma;
struct sg_table tx_sg;
struct sg_table rx_sg;
unsigned cs_change:1;
unsigned tx_nbits:3;
unsigned rx_nbits:3;
#define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */
#define SPI_NBITS_DUAL 0x02 /* 2bits transfer */
#define SPI_NBITS_QUAD 0x04 /* 4bits transfer */
u8 bits_per_word;
u16 delay_usecs;
u32 speed_hz;
struct list_head transfer_list;
};