LCOV - code coverage report
Current view: top level - subsys/pm - pm.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 74 0.0 %
Date: 2022-08-18 11:36:24 Functions: 0 9 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 34 0.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (c) 2018 Intel Corporation.
       3                 :            :  *
       4                 :            :  * SPDX-License-Identifier: Apache-2.0
       5                 :            :  */
       6                 :            : 
       7                 :            : #include <device.h>
       8                 :            : #include <zephyr.h>
       9                 :            : #include <kernel.h>
      10                 :            : #include <timeout_q.h>
      11                 :            : #include <init.h>
      12                 :            : #include <string.h>
      13                 :            : #include <pm/device.h>
      14                 :            : #include <pm/device_runtime.h>
      15                 :            : #include <pm/pm.h>
      16                 :            : #include <pm/state.h>
      17                 :            : #include <pm/policy.h>
      18                 :            : #include <tracing/tracing.h>
      19                 :            : 
      20                 :            : #include "pm_stats.h"
      21                 :            : 
      22                 :            : #include <logging/log.h>
      23                 :            : LOG_MODULE_REGISTER(pm, CONFIG_PM_LOG_LEVEL);
      24                 :            : 
      25                 :            : static ATOMIC_DEFINE(z_post_ops_required, CONFIG_MP_NUM_CPUS);
      26                 :            : static sys_slist_t pm_notifiers = SYS_SLIST_STATIC_INIT(&pm_notifiers);
      27                 :            : 
      28                 :            : /*
      29                 :            :  * Properly initialize cpu power states. Do not make assumptions that
      30                 :            :  * ACTIVE_STATE is 0
      31                 :            :  */
      32                 :            : #define CPU_PM_STATE_INIT(_, __)                \
      33                 :            :         { .state = PM_STATE_ACTIVE }
      34                 :            : static struct pm_state_info z_cpus_pm_state[] = {
      35                 :            :         LISTIFY(CONFIG_MP_NUM_CPUS, CPU_PM_STATE_INIT, (,))
      36                 :            : };
      37                 :            : 
      38                 :            : /* bitmask to check if a power state was forced. */
      39                 :            : static ATOMIC_DEFINE(z_cpus_pm_state_forced, CONFIG_MP_NUM_CPUS);
      40                 :            : #ifdef CONFIG_PM_DEVICE
      41                 :            : static atomic_t z_cpus_active = ATOMIC_INIT(CONFIG_MP_NUM_CPUS);
      42                 :            : #endif
      43                 :            : static struct k_spinlock pm_notifier_lock;
      44                 :            : 
      45                 :            : 
      46                 :            : 
      47                 :            : #ifdef CONFIG_PM_DEVICE
      48                 :            : extern const struct device *__pm_device_slots_start[];
      49                 :            : 
      50                 :            : /* Number of devices successfully suspended. */
      51                 :            : static size_t num_susp;
      52                 :            : 
      53                 :            : static int pm_suspend_devices(void)
      54                 :            : {
      55                 :            :         const struct device *devs;
      56                 :            :         size_t devc;
      57                 :            : 
      58                 :            :         devc = z_device_get_all_static(&devs);
      59                 :            : 
      60                 :            :         num_susp = 0;
      61                 :            : 
      62                 :            :         for (const struct device *dev = devs + devc - 1; dev >= devs; dev--) {
      63                 :            :                 int ret;
      64                 :            : 
      65                 :            :                 /*
      66                 :            :                  * ignore busy devices, wake up source and devices with
      67                 :            :                  * runtime PM enabled.
      68                 :            :                  */
      69                 :            :                 if (pm_device_is_busy(dev) || pm_device_state_is_locked(dev)
      70                 :            :                     || pm_device_wakeup_is_enabled(dev) ||
      71                 :            :                     ((dev->pm != NULL) && pm_device_runtime_is_enabled(dev))) {
      72                 :            :                         continue;
      73                 :            :                 }
      74                 :            : 
      75                 :            :                 ret = pm_device_action_run(dev, PM_DEVICE_ACTION_SUSPEND);
      76                 :            :                 /* ignore devices not supporting or already at the given state */
      77                 :            :                 if ((ret == -ENOSYS) || (ret == -ENOTSUP) || (ret == -EALREADY)) {
      78                 :            :                         continue;
      79                 :            :                 } else if (ret < 0) {
      80                 :            :                         LOG_ERR("Device %s did not enter %s state (%d)",
      81                 :            :                                 dev->name,
      82                 :            :                                 pm_device_state_str(PM_DEVICE_STATE_SUSPENDED),
      83                 :            :                                 ret);
      84                 :            :                         return ret;
      85                 :            :                 }
      86                 :            : 
      87                 :            :                 __pm_device_slots_start[num_susp] = dev;
      88                 :            :                 num_susp++;
      89                 :            :         }
      90                 :            : 
      91                 :            :         return 0;
      92                 :            : }
      93                 :            : 
      94                 :            : static void pm_resume_devices(void)
      95                 :            : {
      96                 :            :         for (int i = (num_susp - 1); i >= 0; i--) {
      97                 :            :                 pm_device_action_run(__pm_device_slots_start[i],
      98                 :            :                                     PM_DEVICE_ACTION_RESUME);
      99                 :            :         }
     100                 :            : 
     101                 :            :         num_susp = 0;
     102                 :            : }
     103                 :            : #endif  /* CONFIG_PM_DEVICE */
     104                 :            : 
     105                 :          0 : static inline void pm_exit_pos_ops(struct pm_state_info *info)
     106                 :            : {
     107                 :            :         extern __weak void
     108                 :            :                 pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id);
     109                 :            : 
     110         [ #  # ]:          0 :         if (pm_state_exit_post_ops != NULL) {
     111                 :          0 :                 pm_state_exit_post_ops(info->state, info->substate_id);
     112                 :            :         } else {
     113                 :            :                 /*
     114                 :            :                  * This function is supposed to be overridden to do SoC or
     115                 :            :                  * architecture specific post ops after sleep state exits.
     116                 :            :                  *
     117                 :            :                  * The kernel expects that irqs are unlocked after this.
     118                 :            :                  */
     119                 :            : 
     120                 :          0 :                 irq_unlock(0);
     121                 :            :         }
     122                 :          0 : }
     123                 :            : 
     124                 :          0 : static inline void state_set(struct pm_state_info *info)
     125                 :            : {
     126                 :            :         extern __weak void
     127                 :            :                 pm_state_set(enum pm_state state, uint8_t substate_id);
     128                 :            : 
     129         [ #  # ]:          0 :         if (pm_state_set != NULL) {
     130                 :          0 :                 pm_state_set(info->state, info->substate_id);
     131                 :            :         }
     132                 :          0 : }
     133                 :            : 
     134                 :            : /*
     135                 :            :  * Function called to notify when the system is entering / exiting a
     136                 :            :  * power state
     137                 :            :  */
     138                 :          0 : static inline void pm_state_notify(bool entering_state)
     139                 :            : {
     140                 :            :         struct pm_notifier *notifier;
     141                 :            :         k_spinlock_key_t pm_notifier_key;
     142                 :            :         void (*callback)(enum pm_state state);
     143                 :            : 
     144                 :          0 :         pm_notifier_key = k_spin_lock(&pm_notifier_lock);
     145   [ #  #  #  #  :          0 :         SYS_SLIST_FOR_EACH_CONTAINER(&pm_notifiers, notifier, _node) {
             #  #  #  # ]
     146         [ #  # ]:          0 :                 if (entering_state) {
     147                 :          0 :                         callback = notifier->state_entry;
     148                 :            :                 } else {
     149                 :          0 :                         callback = notifier->state_exit;
     150                 :            :                 }
     151                 :            : 
     152         [ #  # ]:          0 :                 if (callback) {
     153                 :          0 :                         callback(z_cpus_pm_state[_current_cpu->id].state);
     154                 :            :                 }
     155                 :            :         }
     156                 :          0 :         k_spin_unlock(&pm_notifier_lock, pm_notifier_key);
     157                 :          0 : }
     158                 :            : 
     159                 :          0 : void pm_system_resume(void)
     160                 :            : {
     161                 :          0 :         uint8_t id = _current_cpu->id;
     162                 :            : 
     163                 :            :         /*
     164                 :            :          * This notification is called from the ISR of the event
     165                 :            :          * that caused exit from kernel idling after PM operations.
     166                 :            :          *
     167                 :            :          * Some CPU low power states require enabling of interrupts
     168                 :            :          * atomically when entering those states. The wake up from
     169                 :            :          * such a state first executes code in the ISR of the interrupt
     170                 :            :          * that caused the wake. This hook will be called from the ISR.
     171                 :            :          * For such CPU LPS states, do post operations and restores here.
     172                 :            :          * The kernel scheduler will get control after the ISR finishes
     173                 :            :          * and it may schedule another thread.
     174                 :            :          */
     175         [ #  # ]:          0 :         if (atomic_test_and_clear_bit(z_post_ops_required, id)) {
     176                 :          0 :                 pm_exit_pos_ops(&z_cpus_pm_state[id]);
     177                 :          0 :                 pm_state_notify(false);
     178                 :          0 :                 z_cpus_pm_state[id] = (struct pm_state_info){PM_STATE_ACTIVE,
     179                 :            :                         0, 0};
     180                 :            :         }
     181                 :          0 : }
     182                 :            : 
     183                 :          0 : bool pm_state_force(uint8_t cpu, const struct pm_state_info *info)
     184                 :            : {
     185                 :          0 :         bool ret = false;
     186                 :            : 
     187         [ #  # ]:          0 :         __ASSERT(info->state < PM_STATE_COUNT,
     188                 :            :                  "Invalid power state %d!", info->state);
     189                 :            : 
     190                 :            : 
     191         [ #  # ]:          0 :         if (!atomic_test_and_set_bit(z_cpus_pm_state_forced, cpu)) {
     192                 :          0 :                 z_cpus_pm_state[cpu] = *info;
     193                 :          0 :                 ret = true;
     194                 :            :         }
     195                 :            : 
     196                 :          0 :         return ret;
     197                 :            : }
     198                 :            : 
     199                 :          0 : bool pm_system_suspend(int32_t ticks)
     200                 :            : {
     201                 :          0 :         bool ret = true;
     202                 :          0 :         uint8_t id = _current_cpu->id;
     203                 :            : 
     204                 :            :         SYS_PORT_TRACING_FUNC_ENTER(pm, system_suspend, ticks);
     205                 :            : 
     206         [ #  # ]:          0 :         if (!atomic_test_bit(z_cpus_pm_state_forced, id)) {
     207                 :            :                 const struct pm_state_info *info;
     208                 :            : 
     209                 :          0 :                 info = pm_policy_next_state(id, ticks);
     210         [ #  # ]:          0 :                 if (info != NULL) {
     211                 :          0 :                         z_cpus_pm_state[id] = *info;
     212                 :            :                 }
     213                 :            :         }
     214                 :            : 
     215         [ #  # ]:          0 :         if (z_cpus_pm_state[id].state == PM_STATE_ACTIVE) {
     216         [ #  # ]:          0 :                 LOG_DBG("No PM operations done.");
     217                 :            :                 SYS_PORT_TRACING_FUNC_EXIT(pm, system_suspend, ticks,
     218                 :            :                                    z_cpus_pm_state[id].state);
     219                 :          0 :                 ret = false;
     220                 :          0 :                 atomic_clear_bit(z_cpus_pm_state_forced, id);
     221                 :          0 :                 goto end;
     222                 :            :         }
     223                 :            : 
     224         [ #  # ]:          0 :         if (ticks != K_TICKS_FOREVER) {
     225                 :            :                 /*
     226                 :            :                  * We need to set the timer to interrupt a little bit early to
     227                 :            :                  * accommodate the time required by the CPU to fully wake up.
     228                 :            :                  */
     229                 :          0 :                 z_set_timeout_expiry(ticks -
     230                 :          0 :                      k_us_to_ticks_ceil32(
     231                 :            :                              z_cpus_pm_state[id].exit_latency_us),
     232                 :            :                                      true);
     233                 :            :         }
     234                 :            : 
     235                 :            : #if CONFIG_PM_DEVICE
     236                 :            :         if ((z_cpus_pm_state[id].state != PM_STATE_RUNTIME_IDLE) &&
     237                 :            :                         (atomic_sub(&z_cpus_active, 1) == 1)) {
     238                 :            :                 if (pm_suspend_devices()) {
     239                 :            :                         pm_resume_devices();
     240                 :            :                         z_cpus_pm_state[id].state = PM_STATE_ACTIVE;
     241                 :            :                         (void)atomic_add(&z_cpus_active, 1);
     242                 :            :                         SYS_PORT_TRACING_FUNC_EXIT(pm, system_suspend, ticks,
     243                 :            :                                                    z_cpus_pm_state[id].state);
     244                 :            :                         ret = false;
     245                 :            :                         atomic_clear_bit(z_cpus_pm_state_forced, id);
     246                 :            :                         goto end;
     247                 :            :                 }
     248                 :            :         }
     249                 :            : #endif
     250                 :            :         /*
     251                 :            :          * This function runs with interruptions locked but it is
     252                 :            :          * expected the SoC to unlock them in
     253                 :            :          * pm_state_exit_post_ops() when returning to active
     254                 :            :          * state. We don't want to be scheduled out yet, first we need
     255                 :            :          * to send a notification about leaving the idle state. So,
     256                 :            :          * we lock the scheduler here and unlock just after we have
     257                 :            :          * sent the notification in pm_system_resume().
     258                 :            :          */
     259                 :          0 :         k_sched_lock();
     260                 :          0 :         pm_stats_start();
     261                 :            :         /* Enter power state */
     262                 :          0 :         pm_state_notify(true);
     263                 :          0 :         atomic_set_bit(z_post_ops_required, id);
     264                 :          0 :         state_set(&z_cpus_pm_state[id]);
     265                 :          0 :         pm_stats_stop();
     266                 :            : 
     267                 :            :         /* Wake up sequence starts here */
     268                 :            : #if CONFIG_PM_DEVICE
     269                 :            :         if (atomic_add(&z_cpus_active, 1) == 0) {
     270                 :            :                 pm_resume_devices();
     271                 :            :         }
     272                 :            : #endif
     273                 :          0 :         pm_stats_update(z_cpus_pm_state[id].state);
     274                 :          0 :         pm_system_resume();
     275                 :          0 :         atomic_clear_bit(z_cpus_pm_state_forced, id);
     276                 :          0 :         k_sched_unlock();
     277                 :            :         SYS_PORT_TRACING_FUNC_EXIT(pm, system_suspend, ticks,
     278                 :            :                                    z_cpus_pm_state[id].state);
     279                 :            : 
     280                 :          0 : end:
     281                 :          0 :         return ret;
     282                 :            : }
     283                 :            : 
     284                 :          0 : void pm_notifier_register(struct pm_notifier *notifier)
     285                 :            : {
     286                 :          0 :         k_spinlock_key_t pm_notifier_key = k_spin_lock(&pm_notifier_lock);
     287                 :            : 
     288                 :          0 :         sys_slist_append(&pm_notifiers, &notifier->_node);
     289                 :          0 :         k_spin_unlock(&pm_notifier_lock, pm_notifier_key);
     290                 :          0 : }
     291                 :            : 
     292                 :          0 : int pm_notifier_unregister(struct pm_notifier *notifier)
     293                 :            : {
     294                 :          0 :         int ret = -EINVAL;
     295                 :            :         k_spinlock_key_t pm_notifier_key;
     296                 :            : 
     297                 :          0 :         pm_notifier_key = k_spin_lock(&pm_notifier_lock);
     298         [ #  # ]:          0 :         if (sys_slist_find_and_remove(&pm_notifiers, &(notifier->_node))) {
     299                 :          0 :                 ret = 0;
     300                 :            :         }
     301                 :          0 :         k_spin_unlock(&pm_notifier_lock, pm_notifier_key);
     302                 :            : 
     303                 :          0 :         return ret;
     304                 :            : }
     305                 :            : 
     306                 :          0 : const struct pm_state_info *pm_state_next_get(uint8_t cpu)
     307                 :            : {
     308                 :          0 :         return &z_cpus_pm_state[cpu];
     309                 :            : }

Generated by: LCOV version 1.14