3. SPI控制器驱动注册:platform_driver结构体、spi_alloc_master/spi_register_master接口使用与初始化流程

SPI控制器驱动是Linux SPI子系统与硬件平台之间的桥梁。本章深入剖析SPI控制器驱动的注册机制,重点讲解platform_driver结构体的填充、spi_alloc_master与spi_register_master接口的正确使用,以及完整的初始化流程。掌握这些核心步骤,是编写稳定、可移植的SPI控制器驱动的关键。

3.1 SPI控制器驱动架构概览

在Linux内核中,SPI控制器驱动遵循主机控制器驱动(Host Controller Driver)模型。其核心思想是:通过platform_driver匹配设备树或平台设备,在探测(probe)阶段分配并注册一个SPI主控制器(spi_master),该控制器随后负责管理其总线上的所有SPI从设备。

关键设计原则: SPI控制器驱动不直接操作SPI从设备,而是通过spi_master结构体提供传输接口(transfer_one_message或transfer_one),由SPI核心层调度从设备的I/O请求。
SPI控制器驱动架构分层图 SPI控制器驱动架构分层图 SPI核心层 (spi.c) spi_master 结构体 控制器硬件参数 | 传输队列 | 消息处理函数指针 SPI控制器驱动层 (platform_driver) probe() → 分配/注册master | remove() → 注销master transfer_one() | setup() | prepare_message() SPI硬件控制器 (SoC外设) 驱动注册入口 module_platform_driver()

3.2 platform_driver结构体填充

SPI控制器驱动首先需要定义一个platform_driver结构体实例,并填充必要的回调函数。该结构体是驱动与platform总线交互的接口。

成员 类型 说明 必须实现
probe int (*probe)(struct platform_device *) 设备匹配成功时调用,完成master分配、硬件初始化、master注册 是
remove int (*remove)(struct platform_device *) 设备移除时调用,释放资源、注销master 是
shutdown void (*shutdown)(struct platform_device *) 系统关机或重启时调用,执行优雅关闭 否
driver struct device_driver 包含name、of_match_table、pm等子成员 是
id_table const struct platform_device_id * 传统平台设备匹配表(非设备树场景) 按需
最佳实践: 对于使用设备树的平台,推荐使用.of_match_table进行匹配,并配合MODULE_DEVICE_TABLE(of, ...)导出兼容性列表。这有助于驱动在设备树中自动绑定。

以下是一个典型的SPI控制器platform_driver定义示例:

static const struct of_device_id spi_imx_of_match[] = {
    { .compatible = "fsl,imx6ul-ecspi", .data = &imx6ul_ecspi_devtype_data },
    { .compatible = "fsl,imx8mm-ecspi", .data = &imx8mm_ecspi_devtype_data },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, spi_imx_of_match);

static struct platform_driver spi_imx_driver = {
    .driver = {
        .name = "spi_imx",
        .of_match_table = spi_imx_of_match,
        .pm = &spi_imx_pm_ops,
    },
    .probe = spi_imx_probe,
    .remove = spi_imx_remove,
    .shutdown = spi_imx_shutdown,
};

module_platform_driver(spi_imx_driver);

3.3 spi_alloc_master:分配主控制器对象

spi_alloc_master是SPI核心层提供的分配函数,用于在内存中创建一个spi_master结构体实例,并为其关联的设备私有数据分配空间。

函数原型: struct spi_master *spi_alloc_master(struct device *dev, unsigned int size)
参数说明:
  • dev:父设备指针,通常传入platform_device中的&pdev->dev。
  • size:附加的私有数据区大小(字节),用于存放控制器驱动的自定义结构体。
返回值: 成功返回spi_master指针,失败返回NULL。

使用spi_alloc_master时,驱动开发者需要理解其内存布局:

spi_alloc_master 内存布局图 spi_alloc_master 内存布局 struct spi_master 总线号 | 模式标志 | 时钟频率 传输函数指针 | 队列 | 锁 驱动私有数据 (size字节) 寄存器基地址 | 时钟句柄 DMA描述符 | 状态标志 连续内存 获取私有数据:spi_master_get_devdata(master) → 返回size字节起始地址 通常强制转换为驱动自定义结构体指针
常见错误: 不要直接使用kzalloc(sizeof(struct spi_master), GFP_KERNEL)手动分配spi_master。必须使用spi_alloc_master,因为它会正确初始化核心内部字段、引用计数和锁。手动分配会导致内核崩溃或资源泄漏。

3.4 spi_register_master:注册主控制器

在完成spi_master的字段填充和硬件初始化后,驱动调用spi_register_master将控制器注册到SPI核心子系统。注册成功后,核心层会扫描设备树或ACPI表,枚举该总线上的所有SPI从设备。

函数原型: int spi_register_master(struct spi_master *master)
参数: 已填充好的spi_master指针。
返回值: 0表示成功,负值表示错误码。

注册过程中,核心层会执行以下关键步骤:

  1. 总线号分配:如果master->bus_num为0,核心层自动分配动态总线号;否则使用指定的静态总线号。
  2. 设备树扫描:遍历设备树中该控制器节点下的所有SPI从设备子节点,为每个从设备创建spi_device结构体。
  3. 从设备注册:将spi_device添加到SPI总线,触发SPI从设备驱动的probe匹配。
  4. sysfs接口创建:在/sys/bus/spi/devices/下创建控制器和从设备的属性文件。

3.5 完整初始化流程

SPI控制器驱动的完整初始化流程可以归纳为以下步骤,这些步骤通常在probe函数中顺序执行:

SPI控制器驱动初始化流程图 SPI控制器驱动初始化流程图 (probe函数) ① 获取硬件资源 (platform_get_resource) ② 分配spi_master (spi_alloc_master) ③ 填充spi_master核心字段 ④ 硬件控制器初始化 (时钟、GPIO、DMA) ⑤ 注册spi_master (spi_register_master) ⑥ 平台设备数据绑定 (platform_set_drvdata) probe 返回 0 (成功) 关键字段填充示例 master->bus_num = pdev->id; master->mode_bits = SPI_CPOL | SPI_CPHA; master->transfer_one = xxx_transfer_one; master->setup = xxx_setup; master->num_chipselect = 2;

3.6 核心字段填充详解

在调用spi_register_master之前,驱动必须正确填充spi_master结构体的关键字段。下表列出了最常用的字段及其含义:

字段 类型 说明 典型值/设置
bus_num u16 SPI总线编号,0表示由核心动态分配 pdev->id 或 -1
num_chipselect u16 支持的片选信号数量 硬件支持的CS引脚数
mode_bits u16 支持的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 支持的最小SPI时钟频率 硬件限制,如1000
max_speed_hz u32 支持的最大SPI时钟频率 硬件限制,如50000000
transfer_one 函数指针 单次传输处理函数(推荐) 驱动实现的传输函数
setup 函数指针 从设备参数设置回调 驱动实现的配置函数
prepare_message 函数指针 消息预处理回调(可选) DMA配置等
unprepare_message 函数指针 消息后处理回调(可选) DMA清理等
auto_runtime_pm bool 是否启用运行时PM自动管理 true/false

3.7 完整probe函数示例

以下是一个简化的SPI控制器驱动probe函数实现,展示了上述流程的代码落地:

static int spi_imx_probe(struct platform_device *pdev)
{
    struct spi_master *master;
    struct spi_imx_data *spi_imx;
    struct resource *res;
    int ret;

    /* 1. 分配spi_master,附带私有数据结构体大小 */
    master = spi_alloc_master(&pdev->dev, sizeof(struct spi_imx_data));
    if (!master)
        return -ENOMEM;

    /* 获取私有数据指针 */
    spi_imx = spi_master_get_devdata(master);
    platform_set_drvdata(pdev, master);

    /* 2. 获取硬件资源 */
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    spi_imx->base = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(spi_imx->base)) {
        ret = PTR_ERR(spi_imx->base);
        goto out_master_put;
    }

    spi_imx->irq = platform_get_irq(pdev, 0);
    if (spi_imx->irq < 0) {
        ret = spi_imx->irq;
        goto out_master_put;
    }

    /* 3. 填充spi_master核心字段 */
    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 = 1000;
    master->max_speed_hz = 50000000;
    master->transfer_one = spi_imx_transfer_one;
    master->setup = spi_imx_setup;
    master->prepare_message = spi_imx_prepare_message;
    master->unprepare_message = spi_imx_unprepare_message;
    master->auto_runtime_pm = true;

    /* 4. 硬件初始化:时钟使能、GPIO配置、DMA设置等 */
    ret = spi_imx_hw_init(spi_imx);
    if (ret)
        goto out_master_put;

    /* 5. 注册spi_master到核心层 */
    ret = spi_register_master(master);
    if (ret) {
        dev_err(&pdev->dev, "failed to register spi master: %d\n", ret);
        goto out_hw_deinit;
    }

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

out_hw_deinit:
    spi_imx_hw_deinit(spi_imx);
out_master_put:
    spi_master_put(master);
    return ret;
}
错误处理要点: 在probe函数中,任何步骤失败都必须正确释放已分配的资源。注意spi_alloc_master分配的内存需要通过spi_master_put释放,而不是直接kfree。注册成功后,remove函数中应调用spi_unregister_master。

3.8 remove函数与资源释放

与probe对应的remove函数负责在驱动卸载或设备移除时执行清理:

static int spi_imx_remove(struct platform_device *pdev)
{
    struct spi_master *master = platform_get_drvdata(pdev);
    struct spi_imx_data *spi_imx = spi_master_get_devdata(master);

    /* 1. 注销spi_master,核心层会清理所有从设备 */
    spi_unregister_master(master);

    /* 2. 硬件去初始化 */
    spi_imx_hw_deinit(spi_imx);

    /* 3. 释放spi_master引用 */
    spi_master_put(master);

    return 0;
}
注意: spi_unregister_master会同步等待所有正在进行的传输完成,并移除所有从设备。因此,在调用该函数之前,驱动应确保没有新的传输请求被提交。对于支持运行时PM的驱动,还需在remove中禁用PM。

3.9 调试与验证要点

在SPI控制器驱动开发过程中,以下调试手段可以帮助快速定位问题:

  • 检查dmesg日志:关注probe函数是否成功执行,以及spi_register_master返回的错误码。
  • 查看sysfs节点:注册成功后,/sys/bus/spi/devices/下应出现对应的spi设备目录。
  • 使用devicetree验证:确保设备树中SPI控制器节点的compatible属性与驱动匹配,且子节点格式正确。
  • 调试传输函数:在transfer_one中添加dev_dbg打印,观察传输参数(频率、模式、数据长度)是否正确。
  • 检查时钟和GPIO:使用clk_get_rate确认时钟频率,使用GPIO调试工具确认片选信号电平。
内核调试配置建议: 开启以下内核配置项可以获得更详细的SPI调试信息:
CONFIG_SPI_DEBUG=y — 启用SPI核心层调试输出
CONFIG_DYNAMIC_DEBUG=y — 支持动态开启驱动中的dev_dbg/pr_debug