1. Linux SPI驱动框架概述
SPI(Serial Peripheral Interface)是一种同步串行通信接口,广泛应用于嵌入式系统中连接各种外设。Linux内核提供了完整的SPI子系统支持,包含主控制器驱动(spi_master)和设备驱动(spi_driver)两大部分。本文将重点剖析SPI从设备驱动的注册流程及其核心实现。
SPI总线采用主从架构,通信由主设备(通常是SoC的SPI控制器)发起。在Linux驱动模型中,每个SPI从设备对应一个spi_device结构体,而驱动则通过spi_driver结构体实现。当设备与驱动匹配后,内核会调用驱动的probe函数完成初始化。
提示:SPI总线采用四线制(MOSI、MISO、SCLK、CS),支持全双工通信,时钟频率可达几十MHz,适合高速数据传输场景。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. spi_device结构体解析
2.1 核心字段说明
spi_device结构体定义在include/linux/spi/spi.h中,完整描述了一个SPI从设备的硬件特性:
c复制struct spi_device {
struct device dev; // 内嵌的设备结构体
struct spi_master *master; // 关联的SPI主控制器
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编号
struct spi_statistics statistics; // 通信统计信息
};
2.2 SPI工作模式详解
mode字段使用位掩码定义SPI通信参数,常用标志包括:
c复制#define SPI_CPHA 0x01 // 时钟相位(数据采样边沿)
#define SPI_CPOL 0x02 // 时钟极性(空闲电平)
#define SPI_MODE_0 (0|0) // CPOL=0, CPHA=0
#define SPI_MODE_1 (0|SPI_CPHA) // CPOL=0, CPHA=1
#define SPI_MODE_2 (SPI_CPOL|0) // CPOL=1, CPHA=0
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA) // CPOL=1, CPHA=1
#define SPI_CS_HIGH 0x04 // 片选高电平有效
#define SPI_LSB_FIRST 0x08 // 低位先传输
#define SPI_3WIRE 0x10 // 三线制(共用数据线)
#define SPI_LOOP 0x20 // 回环测试模式
实际应用中,SPI_MODE_0和SPI_MODE_3最为常见。例如,NOR Flash通常使用MODE_0,而某些传感器可能要求MODE_3。配置时需要严格遵循设备手册的时序要求。
2.3 控制器相关字段
controller_state和controller_data字段用于保存控制器私有信息:
- controller_state:保存控制器与设备通信时的硬件状态,如FIFO配置、DMA参数等
- controller_data:存储设备特定的控制器配置,如片选信号延时参数
这些字段通常在设备树或板级初始化代码中配置,驱动开发者一般不需要直接操作。
3. SPI从设备驱动注册流程
3.1 驱动初始化入口
SPI驱动通常以模块形式实现,初始化函数通过module_init宏指定。以spidev驱动为例:
c复制static int __init spidev_init(void)
{
int status;
// 检查次设备号数量是否合法
BUILD_BUG_ON(N_SPI_MINORS > 256);
// 注册字符设备
status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
if (status < 0)
return status;
// 创建设备类
spidev_class = class_create(THIS_MODULE, "spidev");
if (IS_ERR(spidev_class)) {
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
return PTR_ERR(spidev_class);
}
// 注册SPI驱动
status = spi_register_driver(&spidev_spi_driver);
if (status < 0) {
class_destroy(spidev_class);
unregiste
