3. SPI 控制器驱动注册流程:platform_driver_register、spi_alloc_master、spi_register_master 的调用链与初始化步骤
SPI 控制器驱动是 SPI 子系统的核心,负责管理硬件控制器、分配总线号、注册 master 设备,并为后续的 SPI 设备枚举与数据传输提供基础。本章详细拆解从 platform_driver_register 到 spi_register_master 的完整调用链,以及每个阶段的关键初始化步骤。
3.1 整体调用链概览
SPI 控制器驱动的注册遵循 Linux 设备模型的标准流程,其核心调用链如下:
- 入口函数:驱动模块初始化时调用
platform_driver_register,将 SPI 控制器驱动注册到 platform 总线。 - 匹配与 probe:当设备树或 ACPI 表中匹配到对应 compatible 字符串时,内核调用驱动的
probe函数。 - 分配 master:在
probe中调用spi_alloc_master分配struct spi_master结构体,并设置传输参数。 - 硬件初始化:配置控制器寄存器、时钟、DMA、中断等硬件资源。
- 注册 master:调用
spi_register_master将 master 注册到 SPI 核心层,触发总线号分配与设备枚举。
关键点:
spi_alloc_master 与 spi_register_master 必须成对出现,且中间完成所有硬件与软件初始化,否则注册后设备枚举可能失败。
3.2 platform_driver_register:驱动注册入口
SPI 控制器驱动通常以 platform 驱动形式存在,通过 module_platform_driver 宏或直接调用 platform_driver_register 完成注册。该函数将驱动挂载到 platform 总线,等待设备树或 ACPI 匹配。
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 填充 struct platform_driver |
设置 driver.name、driver.of_match_table、probe、remove 回调 |
| 2 | 调用 platform_driver_register |
内核将驱动加入 platform 总线驱动链表,等待设备匹配 |
| 3 | 设备树匹配 | 内核遍历设备树节点,匹配 compatible 属性,调用 probe |
最佳实践:使用
module_platform_driver 宏简化注册,该宏自动处理 module_init 与 module_exit。
3.3 spi_alloc_master:master 结构体分配与初始化
spi_alloc_master 是 SPI 核心层提供的分配函数,原型如下:
struct spi_master *spi_alloc_master(struct device *dev, unsigned int size);
该函数完成以下关键操作:
- 分配内存:为
struct spi_master和驱动私有数据(size 参数)分配连续内存。 - 初始化核心字段:设置
dev.parent、bus_num(默认 -1,由核心自动分配)、num_chipselect、mode_bits等。 - 设置默认回调:
transfer_one_message、set_cs、prepare_message等回调初始化为 NULL,驱动需自行实现。 - 返回 master 指针:驱动可通过
spi_master_get_devdata获取私有数据区域。
注意:
spi_alloc_master 不会分配总线号,总线号在 spi_register_master 时由核心层动态分配或使用驱动指定的 bus_num。
3.4 硬件初始化步骤
在 spi_alloc_master 之后、spi_register_master 之前,驱动必须完成硬件初始化。典型初始化步骤包括:
| 初始化项 | 典型操作 | 注意事项 |
|---|---|---|
| 时钟使能 | clk_prepare_enable |
检查时钟频率是否在 SPI 控制器支持范围内 |
| 复位控制 | 解除复位、配置复位时序 | 某些 SoC 需要复位释放后才能访问寄存器 |
| 中断注册 | devm_request_irq |
中断处理函数需快速完成,或使用 threaded IRQ |
| DMA 配置 | 申请 DMA 通道、配置描述符 | 非必需,但 DMA 可大幅提升大数据传输性能 |
| 控制器寄存器 | 设置模式、时钟极性/相位、位宽 | 通常先配置为默认模式,后续由 SPI 设备覆盖 |
| GPIO 片选 | 配置片选 GPIO 为输出、初始电平 | 若使用 GPIO 片选,需在 master 中设置 cs_gpios |
3.5 spi_register_master:注册与总线号分配
spi_register_master 是注册流程的最后一步,将 master 正式加入 SPI 核心层。其内部执行以下关键步骤:
- 总线号分配:若
master->bus_num为 -1,核心层动态分配一个未使用的总线号;否则使用驱动指定的编号。 - 设备模型注册:将 master 注册为 SPI 总线设备,创建
/sys/bus/spi/devices/下的目录。 - 设备枚举:扫描设备树或 ACPI 表中该 SPI 控制器下的子节点,为每个子节点创建
spi_device。 - 启动队列:初始化消息队列与传输线程,准备接收 SPI 传输请求。
性能提示:
spi_register_master 完成后,SPI 核心层会立即尝试枚举设备。如果硬件初始化不完整(如时钟未稳定),可能导致设备枚举失败或传输异常。建议在注册前确保控制器处于就绪状态。
3.6 完整 probe 函数示例
以下是一个典型的 SPI 控制器驱动 probe 函数骨架,展示了上述三个核心函数的调用顺序:
static int spi_controller_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct spi_controller_data *cdata;
int ret;
// 1. 分配 master 结构体,附带私有数据空间
master = spi_alloc_master(&pdev->dev, sizeof(struct spi_controller_data));
if (!master)
return -ENOMEM;
cdata = spi_master_get_devdata(master);
platform_set_drvdata(pdev, master);
// 2. 设置 master 核心参数
master->bus_num = -1; // 动态分配总线号
master->num_chipselect = 2; // 支持 2 个片选
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
master->transfer_one = spi_controller_transfer_one;
master->set_cs = spi_controller_set_cs;
// 3. 硬件初始化
cdata->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(cdata->clk)) {
ret = PTR_ERR(cdata->clk);
goto err_master_put;
}
clk_prepare_enable(cdata->clk);
cdata->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(cdata->base)) {
ret = PTR_ERR(cdata->base);
goto err_clk_disable;
}
ret = devm_request_irq(&pdev->dev, platform_get_irq(pdev, 0),
spi_controller_isr, 0, pdev->name, master);
if (ret)
goto err_clk_disable;
// 4. 注册 master
ret = spi_register_master(master);
if (ret)
goto err_clk_disable;
dev_info(&pdev->dev, "SPI controller registered, bus %d\n", master->bus_num);
return 0;
err_clk_disable:
clk_disable_unprepare(cdata->clk);
err_master_put:
spi_master_put(master);
return ret;
}
3.7 常见问题与避坑指南
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 总线号冲突 | 多个 master 使用相同 bus_num |
设置 bus_num = -1 让核心自动分配 |
| 设备枚举失败 | 设备树节点 reg 属性与 num_chipselect 不匹配 |
确保 reg 值小于 num_chipselect |
| 传输超时 | 中断未正确注册或时钟未使能 | 检查 devm_request_irq 返回值,确认时钟频率 |
| DMA 传输异常 | DMA 通道申请失败或描述符配置错误 | 回退到 PIO 模式,确保 DMA 缓冲区对齐 |
调试技巧:在
spi_register_master 前后添加 dev_dbg 打印,观察总线号分配与设备枚举日志。使用 ls /sys/bus/spi/devices/ 确认设备是否成功创建。