电源管理

电源管理部分由电源管理子系统所暴露的接口组成。电源管理应用 使用该接口实现电源管理策略。

术语

PMA

系统集成者提供 PMA 。PMA 负责维护所有电源管理相关的策略并基于这些策略执行电源管理相关的动作。PMA 必须被集成到 Zephyr 主应用程序中。

LPS

LPS 指的是 CPU 提供的任何一个低功耗状态。

SoC 功耗状态

Soc 功耗状态描述处理器和设备在 SoC 级实现的功耗状态。

钩子函数

钩子函数是由一个组件实现另一个组件调用的回调函数。例如,PMA 实现的由内核调用的函数。

架构和 SoC 相关的电源状态

x86

活跃
CPU 是活跃的,且正运行在硬件定义的 C0 C-状态。
空转
CPU 不是活跃的,但是继续处于上电状态。CPU 可能正处于某个更低的 C-状态:C1、C2等。
深度睡眠
处理器和系统时钟的电源被关闭,但是 RAM 被保留。

ARM

活跃
CPU 是活跃的,且正在运行。
空转
停止处理器时钟。ARM 的文档将这种状态描述未 睡眠
深度睡眠
停止系统时钟并关闭 PLL 和 flash 内存,但是 RAM 被保留。

ARC

活跃
CPU 当前是活跃的,且运行在 SS0 状态。
空转
定义为 SS1 和 SS2 状态。

这里描述的电源状态是通用术语,它们与基于上面三种架构的处理器和 SoC 所支持的电源状态是对应的。当编写 PMA 代码时,请参考 SoC 的数据手册去获取关于每种功耗状态的详细描述。

概述

Zephyr 电源管理子系统提供了可供系统集成者用于创建 PMA 的接口。然后,PMA 执行任何所需策略。电源管理的设计哲学是不再内核中执行任何策略,将所有权完全交给 PMA。

Zephyr 所提供的基础设置有一个与架构无关的接口。当内核将要进入或退出空转状态时,它会通知 PMA。PMA 可以在这些通知期间执行能执行电源管理策略。

策略

当内核即将进入系统空转状态时,电源管理子系统会通知 PMA,并指定系统将会处于空转状态的时间。PMA 可以在这期间执行任何电源管理操作。PMA 可以执行各种操作,例如让处理器或 SoC 进入低功耗状态,关闭部分或全部外设,关闭设备时钟。通过对这些操作进行组合,PMA 可以创建一个多元的自定义电源管理策略。

不同的节电等级和不同的唤醒延迟都是这些多元策略的特征。通常,节电更多的操作都会导致更多的唤醒延迟。当做策略决定时,PMA 选择节电最多的策略。同时,策略的总执行时间必须在电源管理子系统所分摊的空转时间内。

Zephyr 的电源管理策略根据相对的节电效果和相应的唤醒延迟将策略进行分类。这些策略大致映射为所支持架构的通用处理器和 SoC 的电源状态。PMA 应当将细的自定义策略映射到电源管理子系统的策略分类中。电源管理子系统定义了三种策略。

  • SYS_PM_LOW_POWER_STATE
  • SYS_PM_DEEP_SLEEP
  • SYS_PM_DEVICE_SUSPEND_ONLY

SYS_PM_LOW_POWER_STATE

在这种类策略中,PMA 在部分或全部设备上执行电源管理操作,且让处理器进入一个低功耗状态。设备管理操作可能关闭外设、切断设备时钟。当任何操作有可能导致设备的寄存器状态改变时,这些状态必须被保存并在将来被恢复。PMA 应当映射延迟相对较少的细策略到这种分类。唤醒延迟更对的策略应当映射到 SYS_PM_DEEP_SLEEP`_ 。当 PMA 设置了一个唤醒事件,或者电源管理子系统分配的空转时间到期,或者发生了外部中断,这种分类的策略退出都将退出。

SYS_PM_DEEP_SLEEP

在这种类策略中,PMA 让系统进入 SoC 所支持的深度睡眠状态。在这种状态下,系统时钟被关闭。处理器被关闭,并丢失状态。RAM 应当被保留,以便存储并恢复状态。只有需要将系统从深度睡眠状态中唤醒的设备保持开启;其它所有设备的电源都被 SoC 关闭。由于这样会导致设备的寄存器丢失,它们的状态必须被存储并恢复。PMA 应当将唤醒延迟最高的细策略映射到此策略。当发生 SoC 相关的唤醒事件时,这种分类的策略退出。

SYS_PM_DEVICE_SUSPEND_ONLY

在这种策略中,PAM 在某些设备上执行电源管理操作,但是该操作不对导致处理器或 SoC 的电源状态转换。PAM 应当将唤醒延时最小的细策略映射到此策略。当发生外部中断时,或者电源管理子系统分配的空转时间到期时,这种分类的策略都将退出。

某些策略分类与处理器或 SoC 的电源状态相似,例如 SYS_PM_DEEP_SLEEP 。不过,它们必须被看着策略分类,且不指示任何相相关的处理器或 SoC 电源状态。

电源管理钩子基础设施

这个基础设施由 PMA 实现的钩子函数组成。当内核进入和退出空转状态时(换句话说,内核没有需要调度的线程),电源管理子系统会调用这些钩子函数。本节对钩子函数的概念做一个概述,关于其 API 的具体描述请参考 电源管理 API

挂起钩子函数

int _sys_soc_suspend(int32_t ticks);

当内核即将进入空转状态时,电源管理子系统调用函数 _sys_soc_suspend() 通知 PMA。

此时,内核已经禁止中断且计算完系统最多可以空转多少个时钟滴答。此函数会通知 MPA 时将这个时间传也递给它。当接收到通知后,PMA 选择并执行某个可以在所分配的时间内执行完的电源策略。

电源管理子系统会期待从 _sys_soc_suspend() 返回下列某个值:

SYS_PM_NOT_HANDLED

没有电源管理操作。表明 PMA 在内核所分配的时间内不能完成任何操作。

SYS_PM_DEVICE_SUSPEND_ONLY

只有设备被挂起。表明 PAM 可以执行任何设备挂起操作。这些操作不包括任何处理器或 SoC 的电源操作。

SYS_PM_LOW_POWER_STATE

进入一个低功耗状态。表明 PMA 可以让处理器进入低功耗状态。

SYS_PM_DEEP_SLEEP

进入深度睡眠。表明 PMA 可以将 SoC 进入深度睡眠状态。

恢复钩子函数

void _sys_soc_resume(void);

当从空转状态或低功耗状态退出时,内核会调用该函数。PMA 会根据在 _sys_soc_suspend() 中执行的何种策略需要来执行必须的恢复操作。

由于钩子函数是在中断被禁止时调用的,PMA 应当确保它的操作快速完成。因此,PMA 将确保内核的调度执行不产生混乱。

设备电源管理基础设施

设备电源管理基础设施由 Zephyr 设备模型的接口组成。这些 API 发送控制命令给设备驱动程序,让它们更新电源状态或者返回当前电源状态。关于这些 API 的详细描述请参考 电源管理 API

设备电源管理状态

Zephyr OS 电源管理子系统定义了四种设备状态。这些状态按照其上下文丢失程度、节电时需要完成的操作以及由于状态转换时对设备行为的影响来进行分类。设备上下文包括硬件寄存器、时钟、内存等。

四种设备状态:

DEVICE_PM_ACTIVE_STATE

设备的常规操作。所有的设备上下文被保留。

DEVICE_PM_LOW_POWER_STATE

设备的上下文由硬件存储,不需要驱动程序恢复。

DEVICE_PM_SUSPEND_STATE

大多数设备状态已被硬件丢失。设备驱动程序必须保存并恢复(或者重新初始化)硬件丢失的上下文。

DEVICE_PM_OFF_STATE

电源管理从设备中完全移除。当进入这个状态时,设备的上下文丢失。当设备重新上电后需要重新初始化。

设备电源管理操作

Zephyr OS 提供了一个通用 API 函数给驱动程序发送控制命令。当前支持的控制命令包括:

  • DEVICE_PM_SET_POWER_STATE
  • DEVICE_PM_GET_POWER_STATE

将来可能会支持其它的控制命令。驱动程序可以执行控制命令处理函数,以支持设备驱动的电源管理功能。每个设备驱动程序需要定义:

  • 设备所支持的状态。
  • 设备所支持的在电源状态间的转换。
  • 设备在进行电源状态转换时的必要操作。

下面是设备在进行电源状态转换时可能执行的操作的一个例子:

  • 保存/恢复设备状态。
  • 截断/开启时钟。
  • 截断/开启电源。
  • 屏蔽/解除屏蔽中断。

带电源管理的设备模型

驱动程序使用宏对设备进行初始化,关于该宏的详细使用说明请参考 设备驱动和设备模型 。使用宏 DEVICE_DEFINE 初始化驱动程序时会通过控制函数提供电源管理的支持。宏有一个指针参数指向 device_control 处理函数。

默认初始化器函数

int device_control_nop(struct device *unused_device, uint32_t unused_ctrl_command, void *unused_context);

如果驱动程序没有实现任何电源控制操作,它可以使用默认的 nop 函数初始化相应的指针。默认的初始化函数不会做任何动作,它可以用来替代实现虚假函数,从而避免浪费代码空间。

设备电源管理 API

SoC 接口和应用程序使用这些 API 在设备上执行电源管理操作。

获取设备列表

void device_list_get(struct device **device_list, int *device_count);

Zephyr 内核会在内部维护一个由系统中所有设备构成的链表。PMA 使用这个 API 获取设备链表。PMA 可以使用这个链表对需要执行电源管理操作的设备做标识。

PMA 可以利用这个链表创建一个新的按设备依赖关系排列的链表。PMA 可以创建不同的设备组来执行不同的策略。

注解

确保 PMA 不要更改原始链表,因为内核会使用原始链表。

设备设置电源状态

int device_set_power_state(struct device *device, uint32_t device_power_state);

使用命令 DEVICE_PM_SET_POWER_STATE 调用由设备驱动程序实现的处理函数 device_control()

设备获取电源状态

int device_get_power_state(struct device *device, uint32_t * device_power_state);

使用命令 DEVICE_PM_GET_POWER_STATE 调用由设备驱动程序实现的处理函数 device_control()

忙状态指示

PMA 执行可以关闭设备电源的电源策略时可能导致设备丢失状态。如果关闭电源时设备正在处理硬件业务(例如向 flash 中写数据),可能会使这些业务处于非一致性状态。基础设施能指示 PMA ,告知它设备正在处理这样的硬件业务,从而达到保护业务的目的。

_sys_soc_suspend() 被调用时, PMA 会检查是否有设备处于忙状态。PMA 可以自行决定是执行一个非深度睡眠的其它策略还是延迟电源管理操作(知道下次调用 _sys_soc_suspend() )。

并不是所有的硬件业务都必须被保证。如果有恰当的恢复或挽回方法,驱动程序可以无需对业务进行保证。Zephyr 内核为设备驱动程序和 PMA 提供了如下的 API 判读是否需要保证某个特殊的业务。

指示忙状态的 API

void device_busy_set(struct device *busy_dev);

设置由内核维护的数据结构中的某设备的相应比特,用以指示设备是否正处于某个业务中。

清除忙状态的 API

void device_busy_clear(struct device *busy_dev);

清除由内核维护的数据结构中的某设备的相应比特,用以指示设备没有处于某个业务中。

检查单个设备忙状态的 API

int device_busy_check(struct device *chk_dev);

检查是否某个是被正忙。如果设备不忙返回 0。

检查所有设备忙状态的 API

int device_any_busy_check(void);

检查是否有设备正忙。如果没有设备忙则放回 0。

电源管理配置标志

可以使用下列配置标志单独使能和禁止电源管理功能。

CONFIG_SYS_POWER_MANAGEMENT

该标志使能电源管理子系统。

CONFIG_SYS_POWER_LOW_POWER_STATE

PMA 使能该标志,以使用 SYS_PM_LOW_POWER_STATE 策略。

CONFIG_SYS_POWER_DEEP_SLEEP

该标志使能对 SYS_PM_DEEP_SLEEP 策略的支持。

CONFIG_DEVICE_POWER_MANAGEMENT

如果 PMA 和设备支持设备电源管理,需要使能该标志。

写一个电源管理应用程序

PMA 通过电源管理 API 执行策略。本节详细地描述应用开发者在各种场景下如何写它们自定义的 PMA。

初始化设置

要使能对电源管理的支持,应用程序必须按照如下步骤:

  1. 使能 CONFIG_SYS_POWER_MANAGEMENT 标志。
  2. 使能所需的其它配置标志,参考 电源管理配置标志
  3. 实现钩子函数,参考 电源管理钩子基础设施

设备列表和策略

PMA 使用函数 device_list_get() 恢复系统中使能设备的链表。由于是应用程序的一部分,PMA 会在系统所有设备初始化后才会启动。因此,一旦应用程序启动后,设备链表不会改变。

一旦设备链表被恢复和存储后,PMA 可以构成设备组,并按照设备依赖关系对链表进行排序。PMA 使用设备链表和已知的电源操作唤醒延迟来创建自定义细电源策略。最后,PMA 将将这些自定义策略映射为 `Policies`_ 所描述的电源管理策略。

挂起期间的情景

当电源管理子系统调用 _sys_soc_suspend() 函数后,PMA 可以选择多种场景。

情景 1

所分配的时间太短而不能用于任何电源管理操作。

在这群情况下,PMA 将保持中断的禁止,并返回代码 SYS_PM_NOT_HANDLED 。这个动作允许 Zephyr 内核继续进行常规的空转处理。

情景 2

所分配的时间允许某些设备挂起。

PMA 扫描满足该标志的设备,并使用状态 DEVICE_PM_SUSPEND_STATE 为每个设备调用函数 device_set_power_state()

当所有的设备都挂起后,PMA 执行下列操作:

  • 如果所分配的事件足够执行 SYS_PM_LOW_POWER_STATE 策略:

    1. PMA 设置唤醒事件,让 CPU 进入低功耗装填,并同时重新使能中断。
    2. 返回代码 SYS_PM_LOW_POWER_STATE
  • 如果所分配的时间不足以执行 SYS_PM_LOW_POWER_STATE 策略,PMA 返回代码 SYS_PM_DEVICE_SUSPEND_ONLY

当是被挂起失败,PMA 执行下列操作:

  • 如果系统集成者认为该设备不是必须被挂起,PMA 可以直接忽略之。
  • 如果系统集成者认为该设备必须挂起,PAM 需要采取任何必须的恢复动作,并返回代码 SYS_PM_NOT_HANDLED

情景 3

所分配的时间允许所有设备挂起。

PMA 使用状态 DEVICE_PM_SUSPEND_STATE 为每个设备调用函数 device_set_power_state()

当所有的设备都被挂起,且所分配的时间足够执行 SYS_PM_DEEP_SLEEP 策略,PMA 执行下列操作:

  1. 调用函数 device_any_busy_check() 以获取设备忙状态。如果有设备忙,PMA 必须选择除 SYS_PM_DEEP_SLEEP 之前的某个策略。
  2. 设置唤醒事件。
  3. 让 SoC 处于深度睡眠状态。
  4. 重新使能中断。
  5. 返回代码 SYS_PM_DEEP_SLEEP

如果所分配的时间只能够用于执行能够 SYS_PM_LOW_POWER_STATE 策略,PMA 执行下列操作:

  1. 设置唤醒事件。
  2. 让 CPU 进入某个低功耗状态,并同时重新使能中断。
  3. 返回代码 SYS_PM_LOW_POWER_STATE

如果所分配的时间不够用于 CPU 或 SoC 执行任何调用管理操作,PMA 返回代码 SYS_PM_DEVICE_SUSPEND_ONLY

当设备挂起失败时,PMA 执行下列操作:

  • 如果系统集成者认为该设备不是必须被挂起,PMA 可以直接忽略之。
  • 如果系统集成者认为该设备必须挂起,PAM 需要采取任何必须的恢复动作,并返回代码 SYS_PM_NOT_HANDLED

策略决定总结

PM 操作 策略和返回代码
挂起某些设备并进入低功耗状态 SYS_PM_LOW_POWER_STATE
挂起所有设备并进入低功耗状态 SYS_PM_LOW_POWER_STATE
挂起所有设备并进入深度睡眠 SYS_PM_DEEP_SLEEP
挂起部分或全部设备, 无 CPU/SoC PM 操作 SYS_PM_DEVICE_SUSPEND_ONLY
无 PM 操作 SYS_PM_NOT_HANDLED