设备系统电源管理原理

其实之前我们在介绍系统电源管理的时候已经看到看设备电源管理相关的函数,代码路径zephyr/subsys/pm/power.c:

  1. enum pm_state pm_system_suspend(int32_t ticks)
  2. {
  3. SYS_PORT_TRACING_FUNC_ENTER(pm, system_suspend, ticks);
  4. z_power_state = pm_policy_next_state(ticks);
  5. if (z_power_state.state == PM_STATE_ACTIVE) {
  6. LOG_DBG("No PM operations done.");
  7. SYS_PORT_TRACING_FUNC_EXIT(pm, system_suspend, ticks, z_power_state.state);
  8. return z_power_state.state;
  9. }
  10. post_ops_done = 0;
  11. if (ticks != K_TICKS_FOREVER) {
  12. /*
  13. * Just a sanity check in case the policy manager does not
  14. * handle this error condition properly.
  15. */
  16. __ASSERT(z_power_state.min_residency_us >=
  17. z_power_state.exit_latency_us,
  18. "min_residency_us < exit_latency_us");
  19. /*
  20. * We need to set the timer to interrupt a little bit early to
  21. * accommodate the time required by the CPU to fully wake up.
  22. */
  23. z_set_timeout_expiry(ticks -
  24. k_us_to_ticks_ceil32(z_power_state.exit_latency_us), true);
  25. }
  26. // 设备电源管理
  27. #if CONFIG_PM_DEVICE
  28. bool should_resume_devices = true;
  29. switch (z_power_state.state) {
  30. case PM_STATE_RUNTIME_IDLE:
  31. __fallthrough;
  32. case PM_STATE_SUSPEND_TO_IDLE:
  33. __fallthrough;
  34. case PM_STATE_STANDBY:
  35. /* low power peripherals. */
  36. if (pm_low_power_devices()) {
  37. SYS_PORT_TRACING_FUNC_EXIT(pm, system_suspend,
  38. ticks, _handle_device_abort(z_power_state));
  39. return _handle_device_abort(z_power_state);
  40. }
  41. break;
  42. case PM_STATE_SUSPEND_TO_RAM:
  43. __fallthrough;
  44. case PM_STATE_SUSPEND_TO_DISK:
  45. __fallthrough;
  46. case PM_STATE_SOFT_OFF:
  47. if (pm_suspend_devices()) {
  48. SYS_PORT_TRACING_FUNC_EXIT(pm, system_suspend,
  49. ticks, _handle_device_abort(z_power_state));
  50. return _handle_device_abort(z_power_state);
  51. }
  52. break;
  53. default:
  54. should_resume_devices = false;
  55. break;
  56. }
  57. #endif
  58. /*
  59. * This function runs with interruptions locked but it is
  60. * expected the SoC to unlock them in
  61. * pm_power_state_exit_post_ops() when returning to active
  62. * state. We don't want to be scheduled out yet, first we need
  63. * to send a notification about leaving the idle state. So,
  64. * we lock the scheduler here and unlock just after we have
  65. * sent the notification in pm_system_resume().
  66. */
  67. k_sched_lock();
  68. pm_debug_start_timer();
  69. /* Enter power state */
  70. pm_state_notify(true);
  71. pm_state_set(z_power_state);
  72. pm_debug_stop_timer();
  73. /* Wake up sequence starts here */
  74. // 设备电源管理
  75. #if CONFIG_PM_DEVICE
  76. if (should_resume_devices) {
  77. /* Turn on peripherals and restore device states as necessary */
  78. pm_resume_devices();
  79. }
  80. #endif
  81. pm_log_debug_info(z_power_state.state);
  82. pm_system_resume();
  83. k_sched_unlock();
  84. SYS_PORT_TRACING_FUNC_EXIT(pm, system_suspend, ticks, z_power_state.state);
  85. return z_power_state.state;
  86. }

从上面可以看出来,设备电源管理在进入休眠的时候会根据系统电源管理状态调用两个函数,pm_low_power_devicespm_suspend_devices

pm_low_power_devices函数

  1. int pm_low_power_devices(void)
  2. {
  3. return _pm_devices(PM_DEVICE_STATE_LOW_POWER);
  4. }
  5. static int _pm_devices(enum pm_device_state state)
  6. {
  7. const struct device *devs;
  8. size_t devc;
  9. devc = z_device_get_all_static(&devs);
  10. num_susp = 0;
  11. for (const struct device *dev = devs + devc - 1; dev >= devs; dev--) {
  12. int ret;
  13. /* ignore busy devices */
  14. // 判定设备时忙碌或设备时系统唤醒源
  15. if (pm_device_is_busy(dev) || pm_device_wakeup_is_enabled(dev)) {
  16. continue;
  17. }
  18. // 执行设备的电源管理动作
  19. ret = pm_device_state_set(dev, state);
  20. /* ignore devices not supporting or already at the given state */
  21. if ((ret == -ENOSYS) || (ret == -ENOTSUP) || (ret == -EALREADY)) {
  22. continue;
  23. } else if (ret < 0) {
  24. LOG_ERR("Device %s did not enter %s state (%d)",
  25. dev->name, pm_device_state_str(state), ret);
  26. return ret;
  27. }
  28. __pm_device_slots_start[num_susp] = dev;
  29. num_susp++;
  30. }
  31. return 0;
  32. }

从上面可以看出,当设备是忙碌或者设备是系统的唤醒源是那就不执行设备休眠。
真正执行设备电源管理动作的还是通过pm_device_state_set函数。

pm_suspend_devices函数

  1. int pm_suspend_devices(void)
  2. {
  3. return _pm_devices(PM_DEVICE_STATE_SUSPENDED);
  4. }
  5. static int _pm_devices(enum pm_device_state state)
  6. {
  7. const struct device *devs;
  8. size_t devc;
  9. devc = z_device_get_all_static(&devs);
  10. num_susp = 0;
  11. for (const struct device *dev = devs + devc - 1; dev >= devs; dev--) {
  12. int ret;
  13. /* ignore busy devices */
  14. // 判定设备是否忙碌或设备是否是系统唤醒源
  15. if (pm_device_is_busy(dev) || pm_device_wakeup_is_enabled(dev)) {
  16. continue;
  17. }
  18. // 执行设备电源管理动作
  19. ret = pm_device_state_set(dev, state);
  20. /* ignore devices not supporting or already at the given state */
  21. if ((ret == -ENOSYS) || (ret == -ENOTSUP) || (ret == -EALREADY)) {
  22. continue;
  23. } else if (ret < 0) {
  24. LOG_ERR("Device %s did not enter %s state (%d)",
  25. dev->name, pm_device_state_str(state), ret);
  26. return ret;
  27. }
  28. __pm_device_slots_start[num_susp] = dev;
  29. num_susp++;
  30. }
  31. return 0;
  32. }

从上面可以看出,当设备是忙碌或者设备是系统的唤醒源是那就不执行设备休眠。
真正执行设备电源管理动作的还是通过pm_device_state_set函数。

设备系统电源管理示例

Kconfig定义:

  1. CONFIG_GPIO=y
  2. //启动电源管理
  3. CONFIG_PM=y
  4. //启动设备电源管理
  5. CONFIG_PM_DEVICE=y

设备树:

  1. /{
  2. power-states {
  3. stop0: state0 {
  4. compatible = "zephyr,power-state";
  5. power-state-name = "suspend-to-idle";
  6. substate-id = <1>;
  7. min-residency-us = <500>;
  8. };
  9. stop1: state1 {
  10. compatible = "zephyr,power-state";
  11. power-state-name = "suspend-to-idle";
  12. substate-id = <2>;
  13. min-residency-us = <700>;
  14. };
  15. stop2: state2 {
  16. compatible = "zephyr,power-state";
  17. power-state-name = "suspend-to-idle";
  18. substate-id = <3>;
  19. min-residency-us = <1000>;
  20. };
  21. };
  22. leds {
  23. compatible = "gpio-leds";
  24. green_led_2: led_2 {
  25. gpios = <&gpioa 5 GPIO_ACTIVE_HIGH>;
  26. label = "User LD2";
  27. };
  28. };
  29. aliases {
  30. led0 = &green_led_2;
  31. sw0 = &user_button;
  32. };
  33. }

代码:

  1. /*
  2. * Copyright (c) 2021 Linaro Limited
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <zephyr.h>
  7. #include <device.h>
  8. #include <devicetree.h>
  9. #include <drivers/gpio.h>
  10. #include <sys/printk.h>
  11. #define SLEEP_TIME_MS 2000
  12. static const struct gpio_dt_spec led =
  13. GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);
  14. void main(void)
  15. {
  16. bool led_is_on = true;
  17. __ASSERT_NO_MSG(device_is_ready(led.port));
  18. printk("Device ready\n");
  19. // 标记设备时忙碌的,打开这个注释设备就无法休眠
  20. // pm_device_busy_set(led.port);
  21. while (true) {
  22. gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
  23. gpio_pin_set(led.port, led.pin, (int)led_is_on);
  24. if (led_is_on == false) {
  25. /* Release resource to release device clock */
  26. gpio_pin_configure(led.port, led.pin, GPIO_DISCONNECTED);
  27. }
  28. k_msleep(SLEEP_TIME_MS);
  29. if (led_is_on == true) {
  30. /* Release resource to release device clock */
  31. gpio_pin_configure(led.port, led.pin, GPIO_DISCONNECTED);
  32. }
  33. led_is_on = !led_is_on;
  34. }
  35. }