linux驱动框架分为三部分: 框架核心 ,主控制器驱动,从设备驱动。
- 框架核心:driver/spi/spi.c
- 主机控制器驱动:driver/spi/spi-rockchip.c (举例rk3399的spi)
- 从设备驱动:driver/spi/spidev.c (linux中自带的从设备驱动,功能有限)
这些三部分的实现 离不开 如下结构体:
(路径:include/linux/spi/spi.h)
- struct spi_device
- struct spi_master
- struct spi_driver
- struct spi_transfer
- struct spi_message
- struct spi_board_info
详解介绍:
struct spi_device:
struct spi_device {
struct device dev;
struct spi_master *master; //挂接到哪个spi主控制器上。
u32 max_speed_hz; //从设备支持的最大通讯速率。
u8 chip_select; //片选号,对应struct master ->cs_gpios[]的下标。cs_gpios[]中存放的是用于cs功能的gpio号。
u8 bits_per_word; //每个通讯字的字长,默认是8bit。
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]; //spi从设备的名字
int cs_gpio; /* chip select gpio 本设备片选使用的GPIO号*/
/* the statistics */
struct spi_statistics statistics; //统计使用。
/*
* 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控制器只能跟一个从设备进行沟通。(这点类似I2C总线)
- spi控制器是通过cs 片选引脚的高低来控制和那个设备进行沟通。片选号对应片选引脚。
- struct spi_device ->mode 非常重要,主要是相位(CPHA)和极性(CPOL)的搭配方式。
- struct spi_device ->bits_per_word,指定每次读写的字长,单位bit.如果该值为0,则默认使用8bit为字长。
struct spi_board_info
struct spi_board_info {
/* the device name and module name are coupled, like platform_bus;
* "modalias" is normally the driver name.
*
* platform_data goes to spi_device.dev.platform_data,
* controller_data goes to spi_device.controller_data,
* irq is copied too
*/
char modalias[SPI_NAME_SIZE]; //模块别名
const void *platform_data;
void *controller_data;
int irq; //中断号。
/* slower signaling on noisy or low voltage boards */
u32 max_speed_hz; //最大通讯速率
/* bus_num is board specific and matches the bus_num of some
* spi_master that will probably be registered later.
*
* chip_select reflects how this chip is wired to that master;
* it's less than num_chipselect.
*/
u16 bus_num; //总线号,对应spi主控制器
u16 chip_select; //片选号。
/* mode becomes spi_device.mode, and is essential for chips
* where the default of SPI_CS_HIGH = 0 is wrong.
*/
u16 mode;
/* ... may need additional spi_device chip config data here.
* avoid stuff protocol drivers can set; but include stuff
* needed to behave without being bound to a driver:
* - quirks like clock rate mattering when not selected
*/
};
spi从设备的板信息,这个现在不常用。它的内容基本和struct spi_device一样。
struct spi_master
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; //总线号,一个soc内部会有多个spi主控制器,每一个主控制器可以组成一个spi总线,也就是说每一个总线号就表示一个spi主控制器
/* chipselects will be integral to many controllers; some others
* might use board-specific GPIOs.
*/
u16 num_chipselect; //主控制器最大连接 num_chipselect 个从设备。从设备的片选号不能大于这个数字
/* 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;
/* 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); //设置控制器的工作方式,模式,时钟等,非常重要的一个函数
/* 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
这个函数不可睡眠。
功能:添加消息到队列。
spi框架已经给我们实现了一个transfer函数(spi_queued_transfer()),而且推荐我们使用它提供的transfer函数。
*/
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg);
/* called on release() to free memory provided by spi_master
在spidev_release函数中被调用。
*/
void (*cleanup)(struct spi_device *spi);
/*
* 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.
* 用于启用对DMA处理的核心支持,如果can_dma()存在并返回true,
*则在调用transfer_one()之前将映射传输。驱动程序不应该修改或存储xfer和dma_tx,在准备设备时必须设置dma_rx。
*/
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.
这些钩子用于希望使用通用主传输队列机制的驱动程序。如果使用了这些函数,驱动程序就不能指定上面的transfer()函数。
随着时间的推移,我们希望SPI驱动程序逐步过渡到这个API,也就是内核系统我们使用内核自己提供的spi_master->transfer函数。
*/
bool queued; //是否已经入队的标志
struct kthread_worker kworker; //类似于worker,用于创建一个内核线程。
struct task_struct *kworker_task;
struct kthread_work pump_messages; //将kthread_work 类似于一个work,只是这个kthread_work用于内核线程的处理,这个kthread_work想要处理必须添加到thread_worker的工作链表中,然后被线程处理。
spinlock_t queue_lock;
struct list_head queue;
struct spi_message *cur_msg;
bool idling;
bool busy;
bool running;
bool rt;
bool auto_runtime_pm; //spi主控制器的运行时,电源管理是否执行的标志
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);
/* 如果我们没有实现这个函数,spi框架给我们提供了一个:spi_transfer_one_message()。
spi_transfer_one_message()最终调用struct master -> transfer_one()函数。
所以,如果我们使用spi_transfer_one_message()函数,则必须提供 transfer_one()函数。
*/
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);
/* rk3399实现了这个函数 */
int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
struct spi_transfer *transfer);
void (*handle_err)(struct spi_master *master,
struct spi_message *message);
/* gpio chip select */
int *cs_gpios; //存放作为cs功能的引脚的gpio号。这里是一个数组,这里可以存放 num_chipselect个gpio号。
/* statistics */
struct spi_statistics statistics;
/* DMA channels for use with core dmaengine helpers */
struct dma_chan *dma_tx; //主控制器工作使用的发送通道。当然这些通道也是需要spi主控制器在驱动中申请才能获得,而且最后还要释放
struct dma_chan *dma_rx; //主控制器工作使用的发送通道。当然这些通道也是需要spi主控制器在驱动中申请才能获得,而且最后还要释放
/* dummy data for full duplex devices */
void *dummy_rx;
void *dummy_tx;
};
struct spi_master用来描述一个 spi主控制器。
struct spi_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;
};
struct spi_driver 用来描述一个spi设备驱动。
struct 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; //发送缓存,要发送数据的buff首地址
void *rx_buf; //接受缓存
unsigned len;
dma_addr_t tx_dma; //如果spi_message.is_dma_mapped是真,这个是tx的dma地址
dma_addr_t rx_dma; //如果spi_message.is_dma_mapped是真,这个是rx的dma地址
struct sg_table tx_sg; //用于传输的Scatterlist,目前不用于客户端
struct sg_table rx_sg; //用于接受的Scatterlist,目前不用于客户端
unsigned cs_change:1; //当前spi_transfer发送完成之后重新片选。影响此次传输之后的片选。指示本次transfer结束之后是否要重新片选并调用setup改变设置。这个标志可以减少系统开销
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; //一个通讯字的bit位,默认8bit.
u16 delay_usecs; //发送完成一个spi_transfer后延时时间,此次传输结束和片选改变之间的延时,之后就会启动另一个传输或者结束整个消息
u32 speed_hz; //通信时钟。如果是0,使用默认值
struct list_head transfer_list; //用于链接到spi_message,用来连接的双向链接节点
};
struct spi_tranfer 表示一个读写缓存对。spi_transfer的传送是通过构建一个spi_message来实现的。
spi_transfer的特点如下:
- 一个或多个spi_transfer组成一个spi_message.
- 一个spi_message 就是一个完整的spi传输,也就是一个CS 由高到低,再由低到高的过程。
- spi_transfer 代表一个读写缓冲对,包含接收缓冲区及发送缓冲区,其实,spi_transfer的发送是通过构建spi_message实现。
- 通过将spi_transfer中的链表transfer_list链接到spi_message中的transfers,再以spi_message形势向底层发送数据。
- 每个spi_transfer都可以对传输的一些参数进行设置,使得spi主控制器按照它要求的参数进行数据发送。
- 每一个spi_transfer 都有自己的通讯速率,字宽 的要求
struct spi_message
struct spi_message {
struct list_head transfers; //transfersl链表中挂接的都是struct spi_transfer
struct spi_device *spi; //传输的目标从设备
unsigned is_dma_mapped:1; //spi_transfer 中 tx_dma 和 rx_dma 是否已经 mapped
/* REVISIT: we might want a flag affecting the behavior of the
* last transfer ... allowing things like "read 16 bit length L"
* immediately followed by "read L bytes". Basically imposing
* a specific message scheduling algorithm.
*
* Some controller drivers (message-at-a-time queue processing)
* could provide that as their default scheduling algorithm. But
* others (with multi-message pipelines) could need a flag to
* tell them about such special cases.
*/
/* completion is reported through a callback */
void (*complete)(void *context); //数据传输完成的回调函数
void *context; //提供给complete的可选参数
unsigned frame_length;
unsigned actual_length; //spi_message已经传输了的字节数
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;
};
struct spi_message 描述一次完整的传输,即cs信号从高->底->高的传输。
spi_message 代表 spi 消息,由多个spi_transfer段组成。而且是原子执行,再消息传送完成前不会释放spi总线。
struct spi_transfer 和 struct spi_message的 关系如下:
1.一个spi_message 表示一个完整的传输,也就是CS片选线,由高到低,再由低到高的完整过程。
2.一个spi_message 由一个spi_transfer 或者多个spi_transfer 组成。
一个spi_transfer的的发送过程是:先使能spi主控制器,然后发送,最后禁止spi主控器。
(禁止或使能spi主控制器,不会影响cs片选引脚的电平变化,cs是我们单独控制的。)
但是我们spi驱动的发送是以spi_message为单位进行传输的。
所以:
一个完整的spi传输,如下:
cs ---|_________________________________________|------
|——————————|——————————|——————————|————————| 传输了4个spi_transfer.每个spi_transfer都伴随着spi控制器的使能于禁止。但cs线时钟保持低电平。
4个spi_transfer 传完成,表示spi_message传输完成,则cs拉高。
注意:
1.CS片选引脚的高与低,看spi主控制器,有的是spi控制器的,有的是我们驱动开发者控制的。
2.一个spi_message 类似于一个人跟两一个人的完整的聊天记录。
而spi_transfer 类似于这个聊天过程中的 每一句话。
3.在一个spi_message中的不同spi_transfer 可以设置不同的通讯字宽,速率,缓存,模式,dma.
4.也就是说,一个完整的spi_message的传输过程,中间的速率很可能是不同的哦。
5.所以spi主控制器的配置实在 进行spi_transfer发送前设置的。跟spi_message没有直接的关系
spi驱动框架使用的主要结构体介绍完毕。