3. SPI控制器驱动注册流程

在车载Linux系统中,SPI控制器驱动通常以平台驱动(platform driver)的形式存在,通过标准的内核驱动模型完成注册。核心调用链涉及三个关键函数:platform_driver_register、spi_alloc_master和spi_register_master。理解这一调用链及其参数配置,是编写稳定、高效的车载SPI驱动的基础。

3.1 调用链全景

SPI控制器驱动的注册流程遵循“平台设备绑定 → 主控制器分配 → 主控制器注册”的三步范式。下图展示了完整的调用链与数据流关系:

SPI控制器驱动注册调用链 SPI控制器驱动注册调用链 platform_driver_register probe() 回调函数 spi_alloc_master 分配 struct spi_master 配置 master 参数 spi_register_master 总线匹配 → 设备枚举 关键参数配置项 • bus_num:总线编号 • num_chipselect:片选数量 • mode_bits:支持的模式位 • min/max_speed_hz:速率范围 注:probe() 中完成 spi_alloc_master → 参数配置 → spi_register_master 的完整流程 流程:platform_driver_register → 匹配设备 → probe() → spi_alloc_master → 配置参数 → spi_register_master → 设备枚举

3.2 三步核心调用详解

3.2.1 platform_driver_register:驱动入口注册

SPI控制器驱动首先需要注册为一个平台驱动。通常在模块初始化函数中调用,并填充struct platform_driver结构体:

static struct platform_driver spi_imx_driver = {
    .driver = {
        .name = "imx6ul-spi",
        .of_match_table = spi_imx_of_match,
        .pm = &spi_imx_pm_ops,
    },
    .probe = spi_imx_probe,
    .remove = spi_imx_remove,
    .id_table = spi_imx_devtype,
};

module_platform_driver(spi_imx_driver);
字段 说明 车载场景注意事项
.driver.name 驱动名称,用于和设备树 compatible 匹配 建议与芯片手册命名一致,便于调试
.of_match_table 设备树匹配表,定义 compatible 字符串 车载多平台需包含所有变体
.probe 设备匹配成功后的初始化回调 需包含时钟、复位、DMA等初始化
.id_table 传统平台设备ID表(非设备树时使用) 车载新平台优先使用设备树

3.2.2 spi_alloc_master:主控制器对象分配

在probe回调中,驱动调用spi_alloc_master分配一个struct spi_master对象。该函数接受struct device *dev和unsigned size两个参数:

struct spi_master *master;
int ret;

master = spi_alloc_master(&pdev->dev, sizeof(struct imx_spi_devdata));
if (!master) {
    dev_err(&pdev->dev, "Failed to allocate SPI master\n");
    return -ENOMEM;
}

// 获取私有数据指针
struct imx_spi_devdata *devdata = spi_master_get_devdata(master);
参数说明:
  • dev:父设备指针,通常传入平台设备的 &pdev->dev
  • size:附加私有数据区大小,用于存放控制器硬件相关结构体

分配成功后,通过 spi_master_get_devdata(master) 获取私有数据指针,避免全局变量。

3.2.3 spi_register_master:主控制器注册

完成参数配置后,调用spi_register_master将master注册到SPI核心层。注册成功后,内核会自动扫描设备树中该SPI总线的子节点,并创建对应的SPI从设备:

// 配置master参数(见3.3节)
master->bus_num = pdev->id;
master->num_chipselect = 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->min_speed_hz = 100000;
master->max_speed_hz = 50000000;
master->setup = imx_spi_setup;
master->transfer_one = imx_spi_transfer_one;

// 注册master
ret = spi_register_master(master);
if (ret) {
    dev_err(&pdev->dev, "Failed to register SPI master: %d\n", ret);
    spi_master_put(master);
    return ret;
}

platform_set_drvdata(pdev, master);

3.3 关键参数配置详解

在spi_alloc_master和spi_register_master之间,驱动必须正确配置struct spi_master的各个字段。下表列出了车载场景下最关键的参数:

参数 类型 说明 车载典型值
bus_num u16 SPI总线编号,-1表示动态分配 0, 1, 2(与硬件对应)
num_chipselect u16 支持的片选信号数量 2~4(车载传感器/执行器数量)
mode_bits u32 支持的SPI模式位掩码 SPI_CPOL | SPI_CPHA | SPI_CS_HIGH
bits_per_word_mask u32 支持的每字位数掩码 SPI_BPW_MASK(8) | SPI_BPW_MASK(16)
min_speed_hz u32 最低时钟频率 100000 (100kHz)
max_speed_hz u32 最高时钟频率 50000000 (50MHz)
setup 函数指针 每次传输前配置硬件寄存器 实现时钟极性/相位配置
transfer_one 函数指针 单次SPI传输处理函数 实现FIFO/DMA传输
auto_runtime_pm bool 是否启用运行时电源管理 true(车载低功耗要求)

3.4 车载场景下的参数配置要点

车载工程避坑指南:
  • bus_num冲突:多SOC平台需确保总线编号不重复,建议从设备树获取或使用动态分配(-1)
  • num_chipselect不足:车载T-Box可能同时挂载GPS、加速度计、陀螺仪等多个SPI设备,需预留足够片选
  • 速率范围过窄:高速传感器(如车载摄像头)可能需要50MHz以上,低速设备(如温度传感器)仅需1MHz,需合理设置min/max_speed_hz
  • mode_bits遗漏:部分车载SPI设备使用SPI_3WIRE模式,需在mode_bits中包含

3.5 完整调用链代码模板

以下是一个车载SPI控制器驱动的probe函数模板,展示了完整的调用链与参数配置:

static int imx_spi_probe(struct platform_device *pdev)
{
    struct spi_master *master;
    struct imx_spi_devdata *devdata;
    struct resource *res;
    int ret;

    // 1. 分配spi_master及私有数据
    master = spi_alloc_master(&pdev->dev, sizeof(struct imx_spi_devdata));
    if (!master)
        return -ENOMEM;

    devdata = spi_master_get_devdata(master);
    platform_set_drvdata(pdev, master);

    // 2. 获取硬件资源(时钟、IO内存、中断等)
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    devdata->base = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(devdata->base)) {
        ret = PTR_ERR(devdata->base);
        goto err_out;
    }

    devdata->clk = devm_clk_get(&pdev->dev, NULL);
    if (IS_ERR(devdata->clk)) {
        ret = PTR_ERR(devdata->clk);
        goto err_out;
    }

    // 3. 配置spi_master参数(车载典型配置)
    master->bus_num = pdev->id;                    // 使用平台设备ID
    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->min_speed_hz = 100000;                 // 100kHz
    master->max_speed_hz = 50000000;               // 50MHz
    master->setup = imx_spi_setup;
    master->transfer_one = imx_spi_transfer_one;
    master->auto_runtime_pm = true;                // 启用运行时PM

    // 4. 注册spi_master
    ret = spi_register_master(master);
    if (ret) {
        dev_err(&pdev->dev, "spi_register_master failed: %d\n", ret);
        goto err_out;
    }

    dev_info(&pdev->dev, "SPI controller registered, bus_num=%d\n", master->bus_num);
    return 0;

err_out:
    spi_master_put(master);
    return ret;
}

3.6 调用链时序图

下图以时序方式展示了从驱动加载到设备枚举的完整交互过程:

SPI控制器驱动注册时序 SPI控制器驱动注册时序图 驱动模块 平台核心 SPI核心 设备模型 platform_driver_register 匹配设备 → 调用probe spi_alloc_master 返回spi_master指针 配置master参数 spi_register_master 枚举SPI从设备
关键要点回顾:
  1. platform_driver_register 是驱动入口,通过设备树匹配触发probe
  2. spi_alloc_master 分配主控制器对象,同时分配私有数据区
  3. 参数配置必须在 spi_register_master 之前完成,注册后不可修改
  4. spi_register_master 触发设备枚举,自动创建设备节点
  5. 车载场景需特别关注 bus_num、num_chipselect、mode_bits 和 速率范围