[toc]
drivers/bus/simple-pm-bus.c 简单电源管理总线(Simple PM Bus) 通用的、轻量级的设备电源管理协调器
历史与背景
这项技术是为了解决什么特定问题而诞生的?
simple-pm-bus 的诞生是为了解决在复杂的片上系统(SoC)中一个常见的、但缺乏标准化解决方案的问题:如何以一种通用、有序的方式管理一组相互关联的、但又不属于任何标准总线(如I2C, SPI)的设备的电源状态(主要是休眠与唤醒)。
在许多SoC设计中,存在大量简单的、直接挂在内存映射I/O(MMIO)上的设备。这些设备可能在逻辑上属于一个功能单元(例如,一个视频处理流水线上的多个IP核),它们需要按照特定的顺序进入休眠(suspend)或从中恢复(resume)。
在simple-pm-bus出现之前,处理这种情况通常依赖于:
- 平台代码中的硬编码:在板级支持文件(board file)中硬编码休眠/唤醒的调用顺序,这使得代码难以移植和维护。
- 驱动间的隐式依赖:驱动程序之间通过
msleep等脆弱的方式来猜测和等待其他设备的状态。
simple-pm-bus提供了一个轻量级的、基于设备树的虚拟总线,专门用于解决这个问题。它允许开发者将一组设备“聚合”在一起,并保证在系统级的休眠/唤醒流程中,这些设备会按照它们在设备树中声明的顺序被同步地、有序地休眠和唤醒。
它的发展经历了哪些重要的里程碑或版本迭代?
simple-pm-bus 是由内核开发者 Linus Walleij 引入的,作为对内核电源管理(PM)框架的一个补充。它的发展是增量式的:
- 核心概念实现:最初的版本实现了其核心功能——创建一个虚拟总线,并在总线的回调函数中,按照设备注册的顺序来调用每个子设备的PM回调。
- 与
genpd的集成:一个重要的演进是它与通用电源域(Generic Power Domains,genpd)框架的集成。这使得simple-pm-bus不仅能协调设备的软件休眠状态,还能协同管理它们所依赖的硬件电源域的开关。 - 异步支持的考量与放弃:社区曾讨论过是否为
simple-pm-bus添加异步的休眠/唤醒能力。但最终的结论是,这个总线的核心价值就在于其简单性和保证的顺序性,而异步处理会破坏这一点,且对于真正需要异步的复杂场景,已有其他更好的解决方案(如设备链接device_link)。
目前该技术的社区活跃度和主流应用情况如何?
simple-pm-bus 是一个相对“小众”但非常实用的内核组件。它不像I2C或USB总线那样被普遍知晓,但在嵌入式和SoC领域,它是一个解决特定问题的标准工具。
- 应用情况:在ARM、ARM64等平台的设备树(DTS)文件中,经常可以看到
simple-pm-bus的身影,用于组织那些没有标准总线接口的MMIO设备。 - 社区状态:该代码非常稳定和成熟,改动很少。
核心原理与设计
它的核心工作原理是什么?
simple-pm-bus 的核心是一个**“代理”或“直通(passthrough)”总线**。它自身不实现任何复杂的硬件协议,而是利用VFS和设备模型的框架来协调和转发电源管理事件。
- 设备树声明:一切始于设备树。开发者会创建一个节点,其
compatible属性为"simple-pm-bus"。所有需要被该总线管理的设备,都作为这个节点的子节点来声明。子节点在DTS文件中的物理顺序,就决定了它们被处理的顺序。 - 总线驱动注册:
simple-pm-bus.c中实现了一个平台驱动程序。当内核解析设备树并找到一个simple-pm-bus节点时,这个驱动的probe函数会被调用。 - 虚拟总线创建:
probe函数的主要任务是创建一个struct bus_type的实例,这是一个代表simple-pm-bus的虚拟总线。 - PM回调的实现:这个虚拟总线的关键在于它自己定义了
suspend和resume回调函数。 - 有序的事件转发:
- 当系统休眠时:内核电源管理核心会调用
simple-pm-bus的suspend回调。这个回调函数会按照与设备注册时相反的顺序(后注册的先休眠),遍历所有挂在该总线上的子设备,并依次调用它们各自驱动程序的suspend回调。 - 当系统唤醒时:过程相反。
simple-pm-bus的resume回调会按照设备注册时的顺序(先注册的先唤醒),依次调用子设备的resume回调。
- 当系统休眠时:内核电源管理核心会调用
通过这种方式,simple-pm-bus利用设备模型固有的父子关系和注册顺序,以极低的成本实现了一个有序的PM事件协调器。
它的主要优势体现在哪些方面?
- 保证执行顺序:这是其核心价值。它为一组设备的休眠/唤醒提供了严格的、可预测的顺序保证。
- 简单性:实现非常简单,代码量小,易于理解。它不引入任何新的复杂概念。
- 基于设备树:将设备间的PM依赖关系以一种声明式的方式固化在设备树中,使得硬件描述与驱动代码解耦,提高了可移植性。
- 同步执行:其同步执行模型对于那些必须等待前一个设备完全休眠/唤醒后才能进行下一步操作的场景,是非常简单和可靠的。
它存在哪些已知的劣势、局限性或在特定场景下的不适用性?
- 严格的同步模型:其最大的优势也是其最大的局限性。它不支持并行/异步的休眠和唤醒,因此可能会拖慢整个系统的休眠/唤醒速度。
- 功能单一:它只处理电源管理。它不是一个通用的总线,不提供设备间的通信、中断处理或DMA等功能。
- 不处理运行时PM:
simple-pm-bus主要关注系统级的休眠/唤醒(suspend/resume)。虽然它可以与运行时PM(Runtime PM)共存,但它本身不提供复杂的运行时PM协调逻辑。
使用场景
在哪些具体的业务或技术场景下,它是首选解决方案?
当满足以下所有条件时,simple-pm-bus是首选解决方案:
- 你有一组没有标准硬件总线的设备(通常是MMIO设备)。
- 这些设备在系统进入或退出全局休眠状态时,必须按照一个严格的顺序进行休眠或唤醒。
- 这个顺序是静态的,可以在设备树中预先定义。
- 不需要并行处理来加速休眠/唤醒过程。
示例:
一个视频编码SoC包含三个IP核:一个图像缩放器(Scaler),一个颜色空间转换器(CSC),和一个H.264编码器(Encoder)。它们形成一个流水线。在系统休眠时,必须先关闭Encoder,再关闭CSC,最后关闭Scaler。唤醒时顺序相反。
在设备树中,可以这样描述:
1 | video_pipeline: video-pipeline { |
在这种配置下,simple-pm-bus会确保唤醒时scaler -> csc -> encoder的resume顺序,休眠时encoder -> csc -> scaler的suspend顺序。
是否有不推荐使用该技术的场景?为什么?
- 需要高性能并行处理:如果一组设备的休眠/唤醒可以并行进行以加速系统状态转换,那么
simple-pm-bus的强制串行模型会成为瓶颈。在这种情况下,应该让这些设备独立存在,并依赖内核的通用异步机制。 - 设备间有复杂的运行时依赖:如果设备A的运行时PM状态依赖于设备B的状态(例如,A只有在B处于Active状态时才能被唤醒),那么应该使用更强大的**设备链接(Device Links)**机制,它专门用于管理设备间的运行时依赖。
- 设备属于标准总线:如果设备挂在I2C, SPI, USB等标准总线之上,那么应该使用这些总线自身的电源管理规范,而不是
simple-pm-bus。
对比分析
请将其 与 其他相似技术 进行详细对比。
simple-pm-bus vs. 设备链接 (Device Links)
| 特性 | simple-pm-bus | 设备链接 (Device Links) |
|---|---|---|
| 功能概述 | 一个虚拟总线,用于在系统休眠/唤醒期间,保证一组设备按顺序被回调。 | 一个点对点的依赖关系描述。它声明一个设备(consumer)依赖于另一个设备(supplier)。 |
| 主要解决的问题 | 静态的、有序的系统级PM回调。 | 动态的、运行时的PM依赖。确保supplier在consumer需要它之前被唤醒,在consumer不再需要它之后才休眠。 |
| 工作时机 | 系统休眠/唤醒 (suspend/resume)。 |
运行时PM (runtime_suspend/runtime_resume) 和 系统休眠/唤醒。 |
| 依赖模型 | 多对一(总线)。多个设备属于同一个总线,共享一个顺序。 | 一对一(链接)。可以构建复杂的依赖图(DAG)。 |
| 配置方式 | 在设备树中通过父子节点关系隐式定义顺序。 | 通过设备树中的power-domains或clocks等属性自动创建,或通过device_link_add() API 显式创建。 |
| 适用场景 | 硬件流水线等需要在系统休眠时严格按顺序开关的场景。 | 驱动A需要确保驱动B的设备(如时钟、电源)已经开启后才能继续工作的场景。 |
Simple Power-Managed Bus Driver
本代码片段展示了一个名为 simple-pm-bus 的平台设备驱动(platform driver)。其核心功能是为那些本身没有复杂硬件、主要作为其他设备“容器”的简单总线设备(如 simple-bus),提供一个通用的、基于时钟管理的运行时电源管理(Runtime PM)框架。当总线下的所有子设备都进入空闲状态时,此驱动会自动禁用(gate)总线的所有时钟以节省功耗;当任何一个子设备需要被访问时,它又会自动重新使能这些时钟。
实现原理分析
此驱动是 Linux 内核中一个典型的“胶水层”和“基础设施”驱动,它为一类设备提供了通用的行为,其原理基于设备树、平台驱动模型和运行时电源管理框架。
设备匹配与驱动绑定:
simple_pm_bus_of_match数组定义了此驱动会尝试绑定的设备。它通过compatible字符串来匹配设备树中的节点。- 核心目标:
.compatible = "simple-pm-bus"。任何在设备树中明确标记为simple-pm-bus的节点,都是此驱动的主要服务对象。 - 透明总线处理: 它也匹配
simple-bus,simple-mfd等。但对于这些设备,它设置了.data = ONLY_BUS。在probe函数中,有一个复杂的逻辑if (match && match->data),其效果是:如果一个设备匹配的是这些“透明总线”,并且这个驱动是其最精确的匹配项(of_property_match_string(...) == 0),那么probe函数就什么也不做,直接返回成功。这是一种“占位”行为,确保了这些总线设备有一个驱动与之绑定(满足设备模型的完整性),但将实际的电源管理等任务留给它们自己的驱动(如果存在的话)。
probe函数的核心逻辑:- 当一个真正的
simple-pm-bus设备被探测到时:
a. 获取时钟:devm_clk_bulk_get_all是一个关键函数。它会自动解析设备树中与该总线节点关联的所有clocks属性,并获取这些时钟的句柄。
b. 使能 Runtime PM:pm_runtime_enable(&pdev->dev)激活了内核的运行时电源管理框架。此后,内核会开始自动跟踪该总线设备及其所有子设备的“使用计数”(usage count)。
c. 填充子设备:of_platform_populate(np, NULL, lookup, &pdev->dev)是另一个核心步骤。它会解析当前总线节点的所有子节点,并为每个子节点动态地创建和注册一个新的平台设备。这使得这些子设备能够被它们自己的驱动程序所探测和绑定。
- 当一个真正的
运行时电源管理 (
simple_pm_bus_pm_ops):- 此驱动通过
.pm字段,将其电源管理操作集simple_pm_bus_pm_ops注册到内核。 runtime_suspend: 当内核的 Runtime PM 框架检测到该总线及其所有子设备都已空闲(使用计数降为 0),并且过了自动挂起的延迟时间后,它会自动调用simple_pm_bus_runtime_suspend。此函数的核心是clk_bulk_disable_unprepare,它会关闭之前获取的所有时钟。runtime_resume: 当任何一个子设备(或总线自身)需要被访问(例如,用户空间open一个子设备文件,导致其驱动的probe被调用),内核会首先自动调用父总线的simple_pm_bus_runtime_resume。此函数的核心是clk_bulk_prepare_enable,它会重新使能所有时钟,确保总线和子设备能够正常工作。
- 此驱动通过
代码分析
1 | // ... (包含的头文件) ... |









