Branch data Line data Source code
1 : : /* 2 : : * Copyright (c) 2018 Intel Corporation. 3 : : * Copyright (c) 2022 Nordic Semiconductor ASA 4 : : * 5 : : * SPDX-License-Identifier: Apache-2.0 6 : : */ 7 : : 8 : : #include <kernel.h> 9 : : #include <pm/pm.h> 10 : : #include <pm/policy.h> 11 : : #include <spinlock.h> 12 : : #include <sys_clock.h> 13 : : #include <sys/__assert.h> 14 : : #include <sys/time_units.h> 15 : : #include <sys/atomic.h> 16 : : #include <toolchain.h> 17 : : 18 : : /** State lock reference counting */ 19 : : static atomic_t state_lock_cnt[PM_STATE_COUNT]; 20 : : 21 : : /** Lock to synchronize access to the latency request list. */ 22 : : static struct k_spinlock latency_lock; 23 : : /** List of maximum latency requests. */ 24 : : static sys_slist_t latency_reqs; 25 : : /** Maximum CPU latency in ticks */ 26 : : static int32_t max_latency_ticks = K_TICKS_FOREVER; 27 : : /** Callback to notify when maximum latency changes. */ 28 : : static pm_policy_latency_changed_cb_t latency_changed_cb; 29 : : 30 : : /** @brief Update maximum allowed latency. */ 31 : 0 : static void update_max_latency(void) 32 : : { 33 : 0 : int32_t new_max_latency_ticks = K_TICKS_FOREVER; 34 : : struct pm_policy_latency_request *req; 35 : : 36 [ # # # # : 0 : SYS_SLIST_FOR_EACH_CONTAINER(&latency_reqs, req, node) { # # # # ] 37 [ # # ]: 0 : if ((new_max_latency_ticks == K_TICKS_FOREVER) || 38 [ # # ]: 0 : ((int32_t)req->value < new_max_latency_ticks)) { 39 : 0 : new_max_latency_ticks = (int32_t)req->value; 40 : : } 41 : : } 42 : : 43 [ # # ]: 0 : if ((latency_changed_cb != NULL) && 44 [ # # ]: 0 : (max_latency_ticks != new_max_latency_ticks)) { 45 : : int32_t latency_us; 46 : : 47 [ # # ]: 0 : if (new_max_latency_ticks == K_TICKS_FOREVER) { 48 : 0 : latency_us = SYS_FOREVER_US; 49 : : } else { 50 : 0 : latency_us = (int32_t)k_ticks_to_us_ceil32(new_max_latency_ticks); 51 : : } 52 : : 53 : 0 : latency_changed_cb(latency_us); 54 : : } 55 : : 56 : 0 : max_latency_ticks = new_max_latency_ticks; 57 : 0 : } 58 : : 59 : : #ifdef CONFIG_PM_POLICY_DEFAULT 60 : 0 : const struct pm_state_info *pm_policy_next_state(uint8_t cpu, int32_t ticks) 61 : : { 62 : : uint8_t num_cpu_states; 63 : : const struct pm_state_info *cpu_states; 64 : : 65 : 0 : num_cpu_states = pm_state_cpu_get_all(cpu, &cpu_states); 66 : : 67 [ # # ]: 0 : for (int16_t i = (int16_t)num_cpu_states - 1; i >= 0; i--) { 68 : 0 : const struct pm_state_info *state = &cpu_states[i]; 69 : : uint32_t min_residency, exit_latency; 70 : : 71 [ # # ]: 0 : if (pm_policy_state_lock_is_active(state->state)) { 72 : 0 : continue; 73 : : } 74 : : 75 : 0 : min_residency = k_us_to_ticks_ceil32(state->min_residency_us); 76 : 0 : exit_latency = k_us_to_ticks_ceil32(state->exit_latency_us); 77 : : 78 : : /* skip state if it brings too much latency */ 79 [ # # ]: 0 : if ((max_latency_ticks != K_TICKS_FOREVER) && 80 [ # # ]: 0 : (exit_latency >= max_latency_ticks)) { 81 : 0 : continue; 82 : : } 83 : : 84 [ # # ]: 0 : if ((ticks == K_TICKS_FOREVER) || 85 [ # # ]: 0 : (ticks >= (min_residency + exit_latency))) { 86 : 0 : return state; 87 : : } 88 : : } 89 : : 90 : 0 : return NULL; 91 : : } 92 : : #endif 93 : : 94 : 0 : void pm_policy_state_lock_get(enum pm_state state) 95 : : { 96 : 0 : atomic_inc(&state_lock_cnt[state]); 97 : 0 : } 98 : : 99 : 0 : void pm_policy_state_lock_put(enum pm_state state) 100 : : { 101 : 0 : atomic_t cnt = atomic_dec(&state_lock_cnt[state]); 102 : : 103 : : ARG_UNUSED(cnt); 104 : : 105 [ # # ]: 0 : __ASSERT(cnt >= 1, "Unbalanced state lock get/put"); 106 : 0 : } 107 : : 108 : 0 : bool pm_policy_state_lock_is_active(enum pm_state state) 109 : : { 110 : 0 : return (atomic_get(&state_lock_cnt[state]) != 0); 111 : : } 112 : : 113 : 0 : void pm_policy_latency_request_add(struct pm_policy_latency_request *req, 114 : : uint32_t value) 115 : : { 116 : 0 : req->value = k_us_to_ticks_ceil32(value); 117 : : 118 : 0 : k_spinlock_key_t key = k_spin_lock(&latency_lock); 119 : : 120 : 0 : sys_slist_append(&latency_reqs, &req->node); 121 : 0 : update_max_latency(); 122 : : 123 : 0 : k_spin_unlock(&latency_lock, key); 124 : 0 : } 125 : : 126 : 0 : void pm_policy_latency_request_update(struct pm_policy_latency_request *req, 127 : : uint32_t value) 128 : : { 129 : 0 : k_spinlock_key_t key = k_spin_lock(&latency_lock); 130 : : 131 : 0 : req->value = k_us_to_ticks_ceil32(value); 132 : 0 : update_max_latency(); 133 : : 134 : 0 : k_spin_unlock(&latency_lock, key); 135 : 0 : } 136 : : 137 : 0 : void pm_policy_latency_request_remove(struct pm_policy_latency_request *req) 138 : : { 139 : 0 : k_spinlock_key_t key = k_spin_lock(&latency_lock); 140 : : 141 : 0 : (void)sys_slist_find_and_remove(&latency_reqs, &req->node); 142 : 0 : update_max_latency(); 143 : : 144 : 0 : k_spin_unlock(&latency_lock, key); 145 : 0 : } 146 : : 147 : 0 : void pm_policy_latency_changed(pm_policy_latency_changed_cb_t cb) 148 : : { 149 : 0 : k_spinlock_key_t key = k_spin_lock(&latency_lock); 150 : : 151 : 0 : latency_changed_cb = cb; 152 : : 153 : 0 : k_spin_unlock(&latency_lock, key); 154 : 0 : }