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, ¬ifier->_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 : : }
|