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 设备模型的标准流程,其核心调用链如下:

  1. 入口函数:驱动模块初始化时调用 platform_driver_register,将 SPI 控制器驱动注册到 platform 总线。
  2. 匹配与 probe:当设备树或 ACPI 表中匹配到对应 compatible 字符串时,内核调用驱动的 probe 函数。
  3. 分配 master:在 probe 中调用 spi_alloc_master 分配 struct spi_master 结构体,并设置传输参数。
  4. 硬件初始化:配置控制器寄存器、时钟、DMA、中断等硬件资源。
  5. 注册 master:调用 spi_register_master 将 master 注册到 SPI 核心层,触发总线号分配与设备枚举。
关键点:spi_alloc_master 与 spi_register_master 必须成对出现,且中间完成所有硬件与软件初始化,否则注册后设备枚举可能失败。
SPI 控制器驱动注册流程 SPI 控制器驱动注册调用链 platform_driver_register probe() 匹配与调用 spi_alloc_master 硬件初始化(时钟/中断/DMA) spi_register_master 说明:probe() 中必须按顺序完成分配、初始化、注册三步,缺一不可。

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 核心层。其内部执行以下关键步骤:

  1. 总线号分配:若 master->bus_num 为 -1,核心层动态分配一个未使用的总线号;否则使用驱动指定的编号。
  2. 设备模型注册:将 master 注册为 SPI 总线设备,创建 /sys/bus/spi/devices/ 下的目录。
  3. 设备枚举:扫描设备树或 ACPI 表中该 SPI 控制器下的子节点,为每个子节点创建 spi_device。
  4. 启动队列:初始化消息队列与传输线程,准备接收 SPI 传输请求。
性能提示:spi_register_master 完成后,SPI 核心层会立即尝试枚举设备。如果硬件初始化不完整(如时钟未稳定),可能导致设备枚举失败或传输异常。建议在注册前确保控制器处于就绪状态。
spi_register_master 内部流程 spi_register_master 内部执行步骤 总线号分配 设备模型注册 设备枚举 启动消息队列 注册完成 说明:设备枚举会扫描设备树子节点,为每个 SPI 从设备创建 spi_device。

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/ 确认设备是否成功创建。