xref: /optee_os/core/kernel/pm.c (revision ffc1ebb22ede7f41b17d349272488f9287333422)
1b7c94e43SEtienne Carriere // SPDX-License-Identifier: BSD-2-Clause
2b7c94e43SEtienne Carriere /*
3b7c94e43SEtienne Carriere  * Copyright (c) 2018, Linaro Limited
4b7c94e43SEtienne Carriere  */
5b7c94e43SEtienne Carriere 
6b7c94e43SEtienne Carriere #include <keep.h>
7b7c94e43SEtienne Carriere #include <kernel/panic.h>
8b7c94e43SEtienne Carriere #include <kernel/pm.h>
9b7c94e43SEtienne Carriere #include <mm/core_memprot.h>
10*ffc1ebb2SMarouene Boubakri #include <stdlib.h>
11b7c94e43SEtienne Carriere #include <string.h>
12b7c94e43SEtienne Carriere #include <types_ext.h>
13b7c94e43SEtienne Carriere 
14b7c94e43SEtienne Carriere #define PM_FLAG_SUSPENDED	BIT(0)
15b7c94e43SEtienne Carriere 
16b7c94e43SEtienne Carriere static struct pm_callback_handle *pm_cb_ref;
17b7c94e43SEtienne Carriere static size_t pm_cb_count;
18b7c94e43SEtienne Carriere 
19b7c94e43SEtienne Carriere static void verify_cb_args(struct pm_callback_handle *pm_hdl)
20b7c94e43SEtienne Carriere {
21b7c94e43SEtienne Carriere 	if (is_unpaged((void *)(vaddr_t)pm_change_state) &&
22b7c94e43SEtienne Carriere 	    (!is_unpaged((void *)(vaddr_t)pm_hdl->callback) ||
23b7c94e43SEtienne Carriere 	     (pm_hdl->handle && !is_unpaged(pm_hdl->handle)))) {
24b7c94e43SEtienne Carriere 		EMSG("PM callbacks mandates unpaged arguments: %p %p",
25b7c94e43SEtienne Carriere 		     (void *)(vaddr_t)pm_hdl->callback, pm_hdl->handle);
26b7c94e43SEtienne Carriere 		panic();
27b7c94e43SEtienne Carriere 	}
28b7c94e43SEtienne Carriere }
29b7c94e43SEtienne Carriere 
30b7c94e43SEtienne Carriere void register_pm_cb(struct pm_callback_handle *pm_hdl)
31b7c94e43SEtienne Carriere {
32b7c94e43SEtienne Carriere 	size_t count = pm_cb_count;
33b7c94e43SEtienne Carriere 	struct pm_callback_handle *ref;
34b7c94e43SEtienne Carriere 
35b7c94e43SEtienne Carriere 	verify_cb_args(pm_hdl);
36b7c94e43SEtienne Carriere 
37b7c94e43SEtienne Carriere 	ref = realloc(pm_cb_ref, sizeof(*ref) * (count + 1));
38b7c94e43SEtienne Carriere 	if (!ref)
39b7c94e43SEtienne Carriere 		panic();
40b7c94e43SEtienne Carriere 
41b7c94e43SEtienne Carriere 	ref[count] = *pm_hdl;
42b7c94e43SEtienne Carriere 	ref[count].flags = 0;
43b7c94e43SEtienne Carriere 
44b7c94e43SEtienne Carriere 	pm_cb_count = count + 1;
45b7c94e43SEtienne Carriere 	pm_cb_ref = ref;
46b7c94e43SEtienne Carriere }
47b7c94e43SEtienne Carriere 
48b7c94e43SEtienne Carriere static TEE_Result call_callbacks(enum pm_op op, uint32_t pm_hint,
49b7c94e43SEtienne Carriere 				 enum pm_callback_order order)
50b7c94e43SEtienne Carriere {
51b7c94e43SEtienne Carriere 	struct pm_callback_handle *hdl;
52b7c94e43SEtienne Carriere 	size_t n;
53b7c94e43SEtienne Carriere 	TEE_Result res;
54b7c94e43SEtienne Carriere 
55b7c94e43SEtienne Carriere 	for (n = 0, hdl = pm_cb_ref; n < pm_cb_count; n++, hdl++) {
56b7c94e43SEtienne Carriere 		if (hdl->order != order ||
57b7c94e43SEtienne Carriere 		    (hdl->flags & PM_FLAG_SUSPENDED) == (op == PM_OP_SUSPEND))
58b7c94e43SEtienne Carriere 			continue;
59b7c94e43SEtienne Carriere 
60b7c94e43SEtienne Carriere 		res = hdl->callback(op, pm_hint, hdl);
61b7c94e43SEtienne Carriere 		if (res)
62b7c94e43SEtienne Carriere 			return res;
63b7c94e43SEtienne Carriere 
64b7c94e43SEtienne Carriere 		if (op == PM_OP_SUSPEND)
65b7c94e43SEtienne Carriere 			hdl->flags |= PM_FLAG_SUSPENDED;
66b7c94e43SEtienne Carriere 		else
67b7c94e43SEtienne Carriere 			hdl->flags &= ~PM_FLAG_SUSPENDED;
68b7c94e43SEtienne Carriere 	}
69b7c94e43SEtienne Carriere 
70b7c94e43SEtienne Carriere 	return TEE_SUCCESS;
71b7c94e43SEtienne Carriere }
72b7c94e43SEtienne Carriere 
73b7c94e43SEtienne Carriere TEE_Result pm_change_state(enum pm_op op, uint32_t pm_hint)
74b7c94e43SEtienne Carriere {
75b7c94e43SEtienne Carriere 	enum pm_callback_order cnt;
76b7c94e43SEtienne Carriere 	TEE_Result res;
77b7c94e43SEtienne Carriere 
78b7c94e43SEtienne Carriere 	switch (op) {
79b7c94e43SEtienne Carriere 	case PM_OP_SUSPEND:
80b7c94e43SEtienne Carriere 		for (cnt = PM_CB_ORDER_DRIVER; cnt < PM_CB_ORDER_MAX; cnt++) {
81b7c94e43SEtienne Carriere 			res = call_callbacks(op, pm_hint, cnt);
82b7c94e43SEtienne Carriere 			if (res)
83b7c94e43SEtienne Carriere 				return res;
84b7c94e43SEtienne Carriere 		}
85b7c94e43SEtienne Carriere 		break;
86b7c94e43SEtienne Carriere 	case PM_OP_RESUME:
87b7c94e43SEtienne Carriere 		for (cnt = PM_CB_ORDER_MAX; cnt > PM_CB_ORDER_DRIVER; cnt--) {
88b7c94e43SEtienne Carriere 			res = call_callbacks(op, pm_hint, cnt - 1);
89b7c94e43SEtienne Carriere 			if (res)
90b7c94e43SEtienne Carriere 				return res;
91b7c94e43SEtienne Carriere 		}
92b7c94e43SEtienne Carriere 		break;
93b7c94e43SEtienne Carriere 	default:
94b7c94e43SEtienne Carriere 		panic();
95b7c94e43SEtienne Carriere 	}
96b7c94e43SEtienne Carriere 
97b7c94e43SEtienne Carriere 	return TEE_SUCCESS;
98b7c94e43SEtienne Carriere }
99