SPI 子系统的整体架构
用户空间应用程序
↓
spidev.c (字符设备驱动,可以用内核写好的通用字符设备驱动,也可以自己写)
↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
SPI 核心层 (spi.c)
- 注册/注销 SPI Master
- 注册/注销 SPI 设备
- 管理传输队列
- 提供通用 API
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
↓
SPI Master 驱动 (spi-imx.c, spi-s3c64xx.c 等)
- 硬件初始化
- 传输实现
- 中断处理
↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
硬件控制器 (SPI0, SPI1...)
核心数据结构
1. SPI Master 控制器结构
c
// include/linux/spi/spi.h
struct spi_master { // SPI Master 控制器结构体
struct device dev; // 设备模型
struct list_head list; // 链表节点
/* 控制器属性 */
s16 bus_num; // 总线编号(SPI0, SPI1...)
u16 num_chipselect; // 片选信号数量
u16 dma_alignment; // DMA 对齐要求
u16 mode_bits; // 支持的模式位
u32 bits_per_word_mask; // 支持的每字长度
u32 min_speed_hz; // 最小时钟频率
u32 max_speed_hz; // 最大时钟频率
u16 flags; // 标志位
/* 传输相关 */
spinlock_t bus_lock_spinlock;
struct mutex bus_lock_mutex;
bool bus_lock_flag;
/* 队列化传输支持 */
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 idling;
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 (*setup)(struct spi_device *spi);
int (*transfer)(struct spi_device *spi, struct spi_message *mesg);
void (*cleanup)(struct spi_device *spi);
/* 队列化传输的方法 */
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);
/* 单个传输的方法 */
int (*transfer_one)(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *transfer);
/* GPIO 片选 */
int *cs_gpios;
/* DMA 相关 */
struct dma_chan *dma_tx;
struct dma_chan *dma_rx;
void *dummy_rx;
void *dummy_tx;
};
2. SPI 设备结构
c
// include/linux/spi/spi.h
struct spi_device {
struct device dev; // 设备模型
struct spi_master *master; // 所属 master
u32 max_speed_hz; // 最大时钟频率
u8 chip_select; // 片选号
u8 bits_per_word; // 每字位数
u16 mode; // SPI 模式
int irq; // 中断号
void *controller_state;
void *controller_data;
char modalias[SPI_NAME_SIZE];
int cs_gpio; // 片选 GPIO
};
3. SPI 消息和传输结构
c
// SPI 传输结构(一次传输)
struct spi_transfer {
const void *tx_buf; // 发送缓冲区
void *rx_buf; // 接收缓冲区
unsigned len; // 传输长度
dma_addr_t tx_dma; // DMA 地址
dma_addr_t rx_dma;
struct sg_table tx_sg; // scatter-gather 表
struct sg_table rx_sg;
unsigned cs_change:1; // 传输后改变片选
unsigned tx_nbits:3; // 发送位宽
unsigned rx_nbits:3; // 接收位宽
u8 bits_per_word; // 每字位数
u16 delay_usecs; // 传输后延迟
u32 speed_hz; // 时钟频率
struct list_head transfer_list; // 链表节点
};
// SPI 消息结构(多次传输)
struct spi_message {
struct list_head transfers; // 传输链表
struct spi_device *spi; // 目标设备
unsigned is_dma_mapped:1;
/* 完成回调 */
void (*complete)(void *context);
void *context;
unsigned frame_length;
unsigned actual_length;
int status;
struct list_head queue; // 消息队列节点
void *state;
};
SPI Master 驱动框架流程
1. Master 驱动注册流程
c
// drivers/spi/spi-xxx.c (具体的 Master 驱动)
static int spi_xxx_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct spi_xxx_data *data;
int ret;
/* 1. 分配 spi_master 结构 */
master = spi_alloc_master(&pdev->dev, sizeof(*data));
if (!master)
return -ENOMEM;
/* 2. 获取私有数据 */
data = spi_master_get_devdata(master);
/* 3. 设置 master 属性 */
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
master->bus_num = pdev->id; // 总线号
master->num_chipselect = 4; // 支持 4 个片选
master->dev.of_node = pdev->dev.of_node;
/* 4. 设置传输方法(二选一)*/
// 方式1:老式接口
master->transfer = spi_xxx_transfer;
// 方式2:队列化接口(推荐)
master->transfer_one_message = spi_xxx_transfer_one_message;
// 或者
master->transfer_one = spi_xxx_transfer_one;
/* 5. 设置其他方法 */
master->setup = spi_xxx_setup;
master->cleanup = spi_xxx_cleanup;
master->prepare_transfer_hardware = spi_xxx_prepare_hw;
master->unprepare_transfer_hardware = spi_xxx_unprepare_hw;
/* 6. 硬件初始化 */
data->regs = devm_ioremap_resource(&pdev->dev, res);
data->clk = devm_clk_get(&pdev->dev, NULL);
clk_prepare_enable(data->clk);
/* 7. 注册中断 */
ret = devm_request_irq(&pdev->dev, irq, spi_xxx_irq,
0, dev_name(&pdev->dev), data);
/* 8. 注册 spi_master */
ret = devm_spi_register_master(&pdev->dev, master);
if (ret) {
dev_err(&pdev->dev, "register master failed\n");
goto err;
}
platform_set_drvdata(pdev, master);
return 0;
err:
spi_master_put(master);
return ret;
}
2. SPI 核心层的关键函数
c
// drivers/spi/spi.c
/**
* spi_register_master - 注册 SPI master 控制器
*/
int spi_register_master(struct spi_master *master)
{
static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
struct device *dev = master->dev.parent;
int status;
/* 1. 分配总线号 */
if (master->bus_num < 0) {
master->bus_num = atomic_dec_return(&dyn_bus_id);
}
/* 2. 设置设备类型 */
master->dev.class = &spi_master_class;
master->dev.bus = &spi_bus_type;
/* 3. 初始化队列 */
INIT_LIST_HEAD(&master->queue);
spin_lock_init(&master->queue_lock);
spin_lock_init(&master->bus_lock_spinlock);
mutex_init(&master->bus_lock_mutex);
/* 4. 如果支持队列化传输 */
if (master->transfer) {
// 使用老式传输方法
} else {
// 使用队列化传输
master->queued = true;
spi_master_initialize_queue(master);
}
/* 5. 注册设备 */
dev_set_name(&master->dev, "spi%u", master->bus_num);
status = device_add(&master->dev);
if (status < 0)
goto done;
/* 6. 从设备树扫描子设备 */
of_register_spi_devices(master);
/* 7. 从 ACPI 扫描子设备 */
acpi_register_spi_devices(master);
done:
return status;
}
/**
* spi_sync - 同步传输
*/
int spi_sync(struct spi_device *spi, struct spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);
int status;
message->complete = spi_complete;
message->context = &done;
message->spi = spi;
/* 提交消息到队列 */
status = spi_async_locked(spi, message);
if (status == 0) {
/* 等待完成 */
wait_for_completion(&done);
status = message->status;
}
message->context = NULL;
return status;
}
/**
* spi_async - 异步传输
*/
int spi_async(struct spi_device *spi, struct spi_message *message)
{
struct spi_master *master = spi->master;
int ret;
unsigned long flags;
spin_lock_irqsave(&master->bus_lock_spinlock, flags);
if (master->bus_lock_flag)
ret = -EBUSY;
else
ret = __spi_async(spi, message);
spin_lock_irqrestore(&master->bus_lock_spinlock, flags);
return ret;
}
static int __spi_async(struct spi_device *spi, struct spi_message *message)
{
struct spi_master *master = spi->master;
message->spi = spi;
/* 验证消息 */
if (list_empty(&message->transfers))
return -EINVAL;
/* 提交到队列 */
return __spi_queued_transfer(spi, message);
}
3. 队列化传输机制
c
// drivers/spi/spi.c
/**
* spi_queued_transfer - 队列化传输
*/
static int __spi_queued_transfer(struct spi_device *spi,
struct spi_message *msg)
{
struct spi_master *master = spi->master;
unsigned long flags;
spin_lock_irqsave(&master->queue_lock, flags);
/* 1. 如果 master 未运行,返回错误 */
if (!master->running) {
spin_unlock_irqrestore(&master->queue_lock, flags);
return -ESHUTDOWN;
}
/* 2. 设置消息状态 */
msg->actual_length = 0;
msg->status = -EINPROGRESS;
/* 3. 加入队列 */
list_add_tail(&msg->queue, &master->queue);
/* 4. 如果空闲,启动传输 */
if (!master->busy && need_pump)
kthread_queue_work(&master->kworker, &master->pump_messages);
spin_unlock_irqrestore(&master->queue_lock, flags);
return 0;
}
/**
* spi_pump_messages - 消息泵(内核线程)
*/
static void spi_pump_messages(struct kthread_work *work)
{
struct spi_master *master =
container_of(work, struct spi_master, pump_messages);
unsigned long flags;
bool was_busy = false;
int ret;
/* 1. 获取下一条消息 */
spin_lock_irqsave(&master->queue_lock, flags);
if (list_empty(&master->queue) || !master->running) {
master->busy = false;
master->idling = true;
spin_unlock_irqrestore(&master->queue_lock, flags);
return;
}
/* 2. 取出第一条消息 */
master->cur_msg = list_first_entry(&master->queue,
struct spi_message, queue);
list_del_init(&master->cur_msg->queue);
master->busy = true;
spin_unlock_irqrestore(&master->queue_lock, flags);
/* 3. 准备硬件 */
if (!was_busy && master->prepare_transfer_hardware) {
ret = master->prepare_transfer_hardware(master);
if (ret) {
dev_err(&master->dev, "failed to prepare hardware\n");
goto out;
}
}
/* 4. 准备消息 */
if (master->prepare_message) {
ret = master->prepare_message(master, master->cur_msg);
if (ret) {
dev_err(&master->dev, "failed to prepare message\n");
goto out;
}
master->cur_msg_prepared = true;
}
/* 5. 执行传输 */
ret = master->transfer_one_message(master, master->cur_msg);
if (ret) {
dev_err(&master->dev, "failed to transfer message\n");
goto out;
}
return;
out:
/* 错误处理 */
master->cur_msg->status = ret;
spi_finalize_current_message(master);
}
4. 具体 Master 驱动实现示例
c
// drivers/spi/spi-imx.c (i.MX SPI Master 驱动)
struct spi_imx_data {
struct spi_bitbang bitbang;
struct completion xfer_done;
void __iomem *base;
struct clk *clk_per;
struct clk *clk_ipg;
unsigned long spi_clk;
unsigned int speed_hz;
unsigned int bits_per_word;
unsigned int spi_drctl;
unsigned int count;
void (*tx)(struct spi_imx_data *);
void (*rx)(struct spi_imx_data *);
void *rx_buf;
const void *tx_buf;
unsigned int txfifo; /* number of words pushed in tx FIFO */
/* DMA */
bool usedma;
u32 wml;
struct completion dma_rx_completion;
struct completion dma_tx_completion;
};
/**
* spi_imx_transfer_one - 传输一个 transfer
*/
static int spi_imx_transfer_one(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *transfer)
{
struct spi_imx_data *spi_imx = spi_master_get_devdata(master);
/* 1. 配置传输参数 */
spi_imx->speed_hz = transfer->speed_hz;
spi_imx->bits_per_word = transfer->bits_per_word;
spi_imx->count = transfer->len;
spi_imx->tx_buf = transfer->tx_buf;
spi_imx->rx_buf = transfer->rx_buf;
spi_imx->txfifo = 0;
reinit_completion(&spi_imx->xfer_done);
/* 2. 配置硬件 */
spi_imx_config(spi_imx);
/* 3. 根据是否使用 DMA 选择传输方式 */
if (spi_imx->usedma) {
ret = spi_imx_dma_transfer(spi_imx, transfer);
} else {
/* 启动传输(触发中断) */
spi_imx_push(spi_imx);
/* 等待完成 */
wait_for_completion(&spi_imx->xfer_done);
}
return 0;
}
/**
* spi_imx_isr - 中断处理函数
*/
static irqreturn_t spi_imx_isr(int irq, void *dev_id)
{
struct spi_imx_data *spi_imx = dev_id;
/* 1. 读取接收 FIFO */
while (spi_imx_can_read(spi_imx))
spi_imx->rx(spi_imx);
/* 2. 写入发送 FIFO */
if (spi_imx->count) {
spi_imx_push(spi_imx);
return IRQ_HANDLED;
}
/* 3. 传输完成 */
if (spi_imx_can_read(spi_imx))
return IRQ_HANDLED;
/* 4. 通知完成 */
complete(&spi_imx->xfer_done);
return IRQ_HANDLED;
}
SPI 设备驱动使用 SPI Master
c
// oled_drv.c
static struct spi_device *oled;
static int spidev_probe(struct spi_device *spi)
{
/* 保存 spi_device 指针 */
oled = spi;
/* 配置 SPI 模式 */
spi->mode = SPI_MODE_0;
spi->bits_per_word = 8;
spi->max_speed_hz = 10000000; // 10MHz
spi_setup(spi);
// ... 其他初始化 ...
}
static void spi_write_datas(const unsigned char *buf, int len)
{
/* 使用 SPI 核心层API发送数据 */
spi_write(oled, buf, len);
// ↓
// 内部调用流程:
// spi_write()
// → spi_sync()
// → spi_async()
// → __spi_queued_transfer()
// → 加入队列
// → spi_pump_messages()
// → master->transfer_one_message()
// → 具体硬件驱动的传输函数
// → 中断/DMA 完成
// → complete()
// → spi_sync() 返回
}
SPI 框架的完整调用链
应用层/驱动层调用:
spi_write(spi, buf, len)
↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
SPI 核心层 (spi.c)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
↓
spi_sync(spi, &message)
↓
spi_async(spi, &message)
↓
__spi_queued_transfer(spi, &message)
↓
list_add_tail(&message->queue, &master->queue)
↓
kthread_queue_work(&master->kworker, &master->pump_messages)
↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
内核线程: spi_pump_messages()
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
↓
master->prepare_transfer_hardware(master) // 准备硬件
↓
master->prepare_message(master, message) // 准备消息
↓
master->transfer_one_message(master, message) // 传输消息
↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
具体 Master 驱动 (spi-imx.c)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
↓
spi_imx_transfer_one_message()
↓
遍历 message->transfers 链表
↓
for_each_transfer:
spi_imx_transfer_one(master, spi, transfer)
↓
配置硬件寄存器
↓
启动传输(写 FIFO / 启动 DMA)
↓
等待完成(中断或 DMA 回调)
↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
中断处理
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
↓
spi_imx_isr(irq, dev_id)
↓
读取 RX FIFO
写入 TX FIFO
↓
传输完成?
↓
complete(&spi_imx->xfer_done)
↓
spi_imx_transfer_one() 返回
↓
下一个 transfer 或消息完成
↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
回到 SPI 核心层
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
↓
spi_finalize_current_message(master)
↓
message->complete(message->context) // 回调
↓
spi_sync() 中的 wait_for_completion() 返回
↓
spi_write() 返回到调用者
总结
| 层次 | 职责 | 文件 |
|---|---|---|
| SPI 核心层 | 消息队列管理、同步/异步传输、设备注册 | drivers/spi/spi.c |
| SPI Master驱动 | 硬件初始化、传输实现、中断/DMA处理 | drivers/spi/spi-imx.c 等 |
| SPI 设备驱动 | 使用 SPI API 与设备通信 | 你的 oled_drv.c |
关键点:
- 分层设计:核心层提供统一接口,具体驱动实现硬件细节
- 队列化传输 :现代驱动推荐使用
transfer_one而非老式transfer - 内核线程 :
spi_pump_messages在后台处理消息队列 - 同步/异步 :提供
spi_sync(阻塞)和spi_async(非阻塞)两种方式