以下是Linux SPI子系统从设备树解析到驱动匹配、数据传输的完整流程,包含详细调用链和树状结构:
[系统启动阶段]
└───> [内核初始化]
└─── start_kernel()
└─── rest_init()
└─── kernel_init()
└─── init_post()
└─── do_basic_setup()
└─── driver_init()
└─── spi_init() [drivers/spi/spi.c]
├─── bus_register(&spi_bus_type)
│ ├─── 注册SPI总线类型
│ ├─── 初始化匹配、probe、remove回调
│ └─── 创建/sys/bus/spi目录
│
├─── class_register(&spi_master_class)
│ ├─── 注册SPI主设备类
│ └─── 创建/sys/class/spi_master目录
│
└─── spi_register_driver(&spi_bitbang_driver)
└─── 注册位操作SPI驱动框架
└─── 支持纯软件模拟SPI时序
│
└───> [设备树解析阶段]
├─── of_scan_flat_dt()
│ └─── 扫描扁平设备树结构
│ └─── early_init_dt_scan_nodes()
│ └─── 遍历所有设备树节点
│ └─── of_scan_flat_dt_subnodes()
│ └─── 递归解析子节点
│ └─── __of_scan_flat_dt()
│ └─── of_scan_flat_dt_node()
│ └─── 处理单个设备树节点
│ └─── of_platform_populate()
│ ├─── 转换设备树为platform_device
│ └─── of_platform_bus_create()
│ └─── 递归创建总线和设备
│ └─── of_platform_device_create_pdata()
│ └─── 为SPI控制器创建platform_device
│ ├─── 解析reg、interrupts等属性
│ └─── platform_device_register()
│ └─── 将设备添加到platform总线
│ └─── 触发总线匹配逻辑
│ │
│ └───> [SPI控制器驱动匹配阶段]
│ ├─── platform_driver_register()
│ │ └─── 注册SPI控制器驱动
│ │ └─── spi_gpio_driver_init() [示例:GPIO模拟SPI控制器]
│ │ └─── 初始化GPIO模拟SPI驱动
│ │ └─── 设置platform_driver的probe函数
│ │ └─── spi_gpio_probe()
│ │ ├─── spi_alloc_master(dev, sizeof(struct spi_gpio))
│ │ │ └─── 分配并初始化spi_master结构体
│ │ │ ├─── 设置master->dev.parent = &pdev->dev
│ │ │ ├─── 分配私有数据空间
│ │ │ └─── 初始化基本参数
│ │ │
│ │ ├─── 初始化spi_master操作函数
│ │ │ ├─── master->transfer = spi_gpio_transfer
│ │ │ ├─── master->setup = spi_gpio_setup
│ │ │ └─── 设置其他回调函数
│ │ │
│ │ ├─── 配置SPI控制器硬件
│ │ │ ├─── 配置GPIO方向和初始状态
│ │ │ ├─── 设置时钟极性和相位
│ │ │ └─── 配置片选控制方式
│ │ │
│ │ └─── spi_register_master(master)
│ │ └─── 注册spi_master到内核
│ │ ├─── device_register(&master->dev)
│ │ │ └─── 注册为内核设备
│ │ │ ├─── 创建/sys/class/spi_master/spiX目录
│ │ │ └─── 触发uevent通知
│ │ │
│ │ ├─── spi_match_master_to_boardinfo(master)
│ │ │ └─── 匹配预注册的spi_board_info
│ │ │
│ │ └─── spi_master_setup_devices(master)
│ │ └─── 从设备树创建设备
│ │ │
│ │ └───> [SPI设备创建阶段]
│ │ ├─── of_spi_register_devices(master)
│ │ │ └─── 从设备树解析SPI设备
│ │ │ ├─── 遍历SPI控制器的子节点
│ │ │ └─── 为每个子节点创建spi_device
│ │ │
│ │ ├─── of_spi_match_device(master, node)
│ │ │ └─── 验证设备节点与控制器兼容性
│ │ │
│ │ ├─── spi_new_device(master, info)
│ │ │ ├─── alloc_spi_device(master)
│ │ │ │ └─── 分配spi_device结构体
│ │ │ │ ├─── 设置device.parent = &master->dev
│ │ │ │ └─── 初始化基本参数
│ │ │ │
│ │ │ ├─── of_spi_parse_dt(master, dev, node)
│ │ │ │ └─── 解析设备树属性
│ │ │ │ ├─── dev->max_speed_hz = 提取spi-max-frequency
│ │ │ │ ├─── dev->mode = 提取spi-cpol/spi-cpha
│ │ │ │ ├─── dev->chip_select = 提取片选信息
│ │ │ │ └─── 提取其他SPI相关属性
│ │ │ │
│ │ │ └─── device_add(&dev->dev)
│ │ │ └─── 将设备添加到SPI总线
│ │ │ ├─── 创建/sys/bus/spi/devices/spiX.Y目录
│ │ │ └─── 触发总线匹配逻辑
│ │ │ │
│ │ │ └───> [SPI设备与驱动匹配阶段]
│ │ │ ├─── bus_probe_device(&dev->dev)
│ │ │ │ └─── 触发总线探测设备
│ │ │ │
│ │ │ ├─── device_attach(&dev->dev)
│ │ │ │ └─── 尝试为设备寻找驱动
│ │ │ │
│ │ │ ├─── bus_for_each_drv(&spi_bus_type, NULL, &data, __device_attach)
│ │ │ │ └─── 遍历SPI总线上所有驱动
│ │ │ │
│ │ │ ├─── __device_attach(drv, data)
│ │ │ │ └─── 检查单个驱动是否匹配
│ │ │ │
│ │ │ ├─── driver_match_device(drv, dev)
│ │ │ │ └─── 调用SPI总线的匹配函数
│ │ │ │ └─── spi_match_device(drv, dev)
│ │ │ │ └─── 执行SPI设备匹配逻辑
│ │ │ │ ├─── 检查driver的id_table
│ │ │ │ └─── 检查driver的of_match_table
│ │ │ │ └─── 与设备树compatible属性比较
│ │ │ │ │
│ │ │ │ └───> [匹配成功:触发驱动probe]
│ │ │ │ ├─── driver_probe_device(drv, dev)
│ │ │ │ │ └─── 调用驱动的probe函数
│ │ │ │ │
│ │ │ │ ├─── really_probe(dev, drv)
│ │ │ │ │ ├─── 检查驱动是否支持该设备
│ │ │ │ │ ├─── 获取设备锁
│ │ │ │ │ ├─── 调用drv->probe(dev)
│ │ │ │ │ └─── 完成probe后的清理工作
│ │ │ │ │
│ │ │ │ └─── drv->probe(dev)
│ │ │ │ └─── oled_probe() [示例:OLED设备驱动]
│ │ │ │ ├─── 分配和初始化设备私有数据
│ │ │ │ │ └─── dev_get_drvdata(dev) = kzalloc(sizeof(struct oled_data), GFP_KERNEL)
│ │ │ │ │
│ │ │ │ ├─── spi_setup(dev)
│ │ │ │ │ └─── 配置SPI传输参数
│ │ │ │ │ ├─── 设置传输位宽
│ │ │ │ │ ├─── 设置时钟频率
│ │ │ │ │ └─── 确认SPI模式
│ │ │ │ │
│ │ │ │ ├─── 硬件初始化序列
│ │ │ │ │ ├─── 复位OLED控制器
│ │ │ │ │ ├─── 发送初始化命令
│ │ │ │ │ └─── 配置显示参数
│ │ │ │ │
│ │ │ │ ├─── 创建sysfs属性
│ │ │ │ │ └─── device_create_file(dev, &dev_attr_brightness)
│ │ │ │ │
│ │ │ │ └─── 注册字符设备或其他接口
│ │ │ │ ├─── alloc_chrdev_region()
│ │ │ │ ├─── cdev_init()
│ │ │ │ ├─── cdev_add()
│ │ │ │ └─── class_create()
│ │ │ │ └─── 完成后用户空间可见/dev/spidevX.Y
│ │ │ │
│ │ │ └───> [匹配失败:设备未绑定驱动]
│ │ │ └─── 设备保持未驱动状态
│ │ │ └─── 可通过/sys/bus/spi/drivers_probe手动绑定
│ │ │
│ │ └───> [SPI设备就绪:可进行数据传输]
│ │ ├─── 用户空间打开设备文件
│ │ │ └─── open("/dev/spidevX.Y", O_RDWR)
│ │ │
│ │ ├─── 通过ioctl配置SPI参数
│ │ │ ├─── SPI_IOC_WR_MODE: 设置SPI模式
│ │ │ ├─── SPI_IOC_WR_MAX_SPEED_HZ: 设置最大速率
│ │ │ └─── 其他配置选项
│ │ │
│ │ └─── 通过read/write/ioctl进行数据传输
│ │ ├─── write(): 发送数据
│ │ ├─── read(): 接收数据
│ │ └─── ioctl(fd, SPI_IOC_MESSAGE(N), ...): 批量传输
│ │ └─── 构建并发送spi_message
│ │ └─── 调用spi_sync()或spi_async()
│ │ └─── 最终调用master->transfer()完成实际传输
of_spi_register_devices()
→ spi_new_device()
→ device_add()
→ bus_probe_device()
→ device_attach()
→ driver_match_device()
→ spi_match_device()
→ of_match_device()
→ really_probe()
→ oled_probe()
→ spi_setup()
→ 硬件初始化
→ 注册字符设备
[设备树解析阶段]
└───> [解析SPI控制器节点]
├─── of_find_node_by_path("/spi@12340000")
│ └─── 解析SPI控制器属性(compatible, reg, interrupts等)
│
└───> [解析SPI设备子节点]
├─── of_get_next_child()
│ └─── 遍历所有SPI设备子节点
│
└───> [为每个SPI设备创建spi_device]
├─── of_spi_register_devices()
│ ├─── of_spi_match_device()
│ ├─── spi_new_device()
│ │ ├─── alloc_spi_device()
│ │ ├─── 解析设备树属性到spi_device
│ │ │ ├─── spi_device->max_speed_hz
│ │ │ ├─── spi_device->mode
│ │ │ └─── spi_device->chip_select
│ │ │
│ │ └─── device_add()
│ │ └─── bus_probe_device()
│ │ └─── device_attach()
│ │ └─── bus_for_each_drv()
│ │ └─── __device_attach()
│ │ └─── driver_match_device()
│ │ └─── spi_match_device()
│ │ └─── of_match_device()
│ │
│ └─── spi_drv_probe()
│ └─── driver_probe_device()
│ └─── really_probe()
│ └─── driver->probe() [调用spi_driver的probe函数]
│
└───> [SPI设备与驱动匹配成功]
└─── [执行设备特定初始化]
└─── oled_probe() [示例:OLED设备驱动]
├─── spi_setup()
├─── 分配和初始化设备私有数据
├─── 硬件初始化序列
└─── 注册字符设备或其他接口
用户空间: open(“/dev/spidevX.Y”)
→ 内核空间: spidev_open()
→ 用户空间: write()/read()/ioctl()
→ 内核空间: spidev_write()/spidev_read()/spidev_ioctl_message()
→ spi_sync()/spi_async()
→ master->transfer() [如spi_gpio_transfer()]
→ 硬件操作: 移位数据、控制片选、生成时钟
[用户空间应用程序]
└───> [打开SPI设备文件]
└─── open("/dev/spidev0.0", O_RDWR)
└─── [通过VFS调用内核]
└─── spidev_open()
└─── 查找并关联spi_device
│
└───> [数据传输]
├─── [单次传输]
│ └─── write()/read()
│ └─── spidev_write()/spidev_read()
│ └─── spi_sync()
│ └─── spi_async()
│ └─── __spi_async()
│ ├─── queue_spi_message()
│ └─── spi_master_start()
│ └─── master->transfer() [调用控制器的transfer函数]
│ └─── spi_gpio_transfer() [示例:GPIO模拟SPI]
│ ├─── 配置片选
│ ├─── 设置时钟和模式
│ ├─── 位操作实现SPI时序
│ └─── 完成传输回调
│
└─── [批量传输]
└─── ioctl(fd, SPI_IOC_MESSAGE(N), ...)
└─── spidev_ioctl_message()
└─── spi_sync_transfer()
└─── 同单次传输流程
[核心数据结构关系]
┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐
│ struct device │ │ struct bus_type │ │ struct class │
└─────────┬─────────┘ └─────────┬─────────┘ └─────────┬─────────┘
│ │ │
▼ ▼ ▼
┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐
│ struct platform │ │ struct spi_bus │ │ struct spi_master│
│ _device │ │ _type │ └─────────┬─────────┘
└─────────┬─────────┘ └─────────┬─────────┘ │
│ │ ┌─────────┴─────────┐
▼ ▼ │ struct spi_device│
┌───────────────────┐ ┌───────────────────┐ └─────────┬─────────┘
│ struct platform │ │ struct spi │ │
│ _driver │ │ _driver │ ▼
└─────────┬─────────┘ └─────────┬─────────┘ ┌───────────────────┐
│ │ │ struct spi_message│
▼ ▼ └─────────┬─────────┘
┌───────────────────┐ ┌───────────────────┐ │
│ probe() │ │ probe() │ ▼
│ (初始化控制器) │ │ (初始化设备) │ ┌───────────────────┐
└─────────┬─────────┘ └─────────┬─────────┘ │ struct spi_transfer│
│ │ └───────────────────┘
▼ ▼
┌───────────────────┐ ┌───────────────────┐
│ spi_alloc_master()│ │ spi_setup() │
└─────────┬─────────┘ └─────────┬─────────┘
│ │
▼ ▼
┌───────────────────┐ ┌───────────────────┐
│ spi_register_master│ │ spi_sync() │
└───────────────────┘ └─────────┬─────────┘
│
▼
┌───────────────────┐
│ master->transfer()│
└───────────────────┘
plaintext start_kernel() → rest_init() → kernel_init() → driver_init()
→ spi_init() → bus_register(&spi_bus_type) →
class_register(&spi_master_class) →
spi_register_driver(&spi_bitbang_driver)
of_scan_flat_dt()
→ of_platform_populate()
→ of_platform_device_create_pdata()
→ platform_device_register()
→ platform_driver_register()
→ spi_gpio_probe()
→ spi_alloc_master()
→ 配置spi_master操作函数
→ spi_register_master()
→ device_register()
→ spi_master_setup_devices()
of_spi_register_devices()
→ spi_new_device()
→ device_add()
→ bus_probe_device()
→ device_attach()
→ driver_match_device()
→ spi_match_device()
→ of_match_device()
→ really_probe()
→ oled_probe()
→ spi_setup()
→ 硬件初始化
→ 注册字符设备
用户空间: open(“/dev/spidevX.Y”)
→ 内核空间: spidev_open()
→ 用户空间: write()/read()/ioctl()
→ 内核空间: spidev_write()/spidev_read()/spidev_ioctl_message()
→ spi_sync()/spi_async()
→ master->transfer() [如spi_gpio_transfer()]
→ 硬件操作: 移位数据、控制片选、生成时钟
start_kernel()
:内核初始化入口,触发后续所有子系统初始化。rest_init()
:创建内核线程(如 kernel_init
),分离初始化与调度逻辑。kernel_init()
:用户空间初始化入口,调用 init_post()
进入用户态准备。driver_init()
:初始化内核驱动框架,调用各总线/类的初始化函数。spi_init()
(drivers/spi/spi.c
):SPI 子系统核心初始化:
bus_register(&spi_bus_type)
:注册 SPI 总线(struct bus_type
),为设备/驱动匹配提供基础。class_register(&spi_master_class)
:注册 SPI 主设备类(struct class
),用于 /sys/class/spi_master
管理。spi_register_driver(&spi_bitbang_driver)
:预注册 位bang驱动(纯软件模拟SPI时序,适配无硬件控制器场景)。platform_device
创建of_scan_flat_dt()
:扫描设备树扁平结构,触发节点解析。of_platform_populate()
:递归遍历设备树,将节点转换为 platform_device
:
of_platform_device_create_pdata()
:为每个 SPI 控制器节点(如 /spi@12340000
)创建 platform_device
,填充 resource
(寄存器、中断等)。spi-gpio
为例)platform_driver_register()
:注册 SPI 控制器驱动(如 spi_gpio_driver
),其 probe
函数会被触发。spi_gpio_probe()
:初始化 GPIO 模拟的 SPI 控制器:
spi_alloc_master()
:分配 struct spi_master
(SPI 控制器核心结构体)。spi_master
成员:
transfer = spi_gpio_transfer
:实现硬件时序(软件模拟 SPI 读写)。num_chipselect
:支持的片选数量(如 1 个片选引脚)。spi_register_master()
:将 spi_master
注册到内核,触发设备自动创建。spi_master_setup_devices()
:遍历设备树中 SPI 控制器的子节点,创建设备:
of_spi_register_devices()
:解析子节点(如 /spi@12340000/oled
)。spi_new_device()
:分配 struct spi_device
,并解析设备树属性:
max_speed_hz
:从 spi-max-frequency
获取。mode
:从 spi-cpol
/spi-cpha
获取。chip_select
:片选引脚编号。device_add()
:将 spi_device
注册到内核,触发驱动匹配。bus_probe_device()
:触发 SPI 总线的设备探测逻辑。spi_match_device()
:匹配逻辑(优先设备树 compatible
):
spi_driver
,对比 of_match_table
与设备树 compatible
。probe
(以 OLED 为例)driver_probe_device()
:调用 spi_driver
的 probe
函数(如 oled_probe
):
spi_setup()
:配置 SPI 传输参数(速率、模式等)。cdev_add()
暴露 /dev/spidevX.X
接口。SPI控制器未识别
SPI设备无法通信
数据传输错误
这个流程涵盖了从设备树解析到数据传输的完整生命周期,理解这些步骤对于开发SPI驱动或调试SPI设备非常关键。如果需要深入了解某个环节,可以进一步探讨!