xref: /optee_os/core/kernel/pm.c (revision 5920ec258f16b84f301ae95a37ffc14802e4f8a6)
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>
9*5920ec25SEtienne Carriere #include <malloc.h>
10b7c94e43SEtienne Carriere #include <mm/core_memprot.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 
19502e23adSEtienne Carriere static const char no_name[] = "no-name";
20502e23adSEtienne Carriere DECLARE_KEEP_PAGER(no_name);
21502e23adSEtienne Carriere 
22b7c94e43SEtienne Carriere static void verify_cb_args(struct pm_callback_handle *pm_hdl)
23b7c94e43SEtienne Carriere {
24b7c94e43SEtienne Carriere 	if (is_unpaged((void *)(vaddr_t)pm_change_state) &&
25b7c94e43SEtienne Carriere 	    (!is_unpaged((void *)(vaddr_t)pm_hdl->callback) ||
26b7c94e43SEtienne Carriere 	     (pm_hdl->handle && !is_unpaged(pm_hdl->handle)))) {
27b7c94e43SEtienne Carriere 		EMSG("PM callbacks mandates unpaged arguments: %p %p",
28b7c94e43SEtienne Carriere 		     (void *)(vaddr_t)pm_hdl->callback, pm_hdl->handle);
29b7c94e43SEtienne Carriere 		panic();
30b7c94e43SEtienne Carriere 	}
31b7c94e43SEtienne Carriere }
32b7c94e43SEtienne Carriere 
33b7c94e43SEtienne Carriere void register_pm_cb(struct pm_callback_handle *pm_hdl)
34b7c94e43SEtienne Carriere {
35502e23adSEtienne Carriere 	struct pm_callback_handle *ref = NULL;
36502e23adSEtienne Carriere 	const char *name = pm_hdl->name;
37b7c94e43SEtienne Carriere 	size_t count = pm_cb_count;
38b7c94e43SEtienne Carriere 
39b7c94e43SEtienne Carriere 	verify_cb_args(pm_hdl);
40b7c94e43SEtienne Carriere 
41502e23adSEtienne Carriere 	if (!name)
42502e23adSEtienne Carriere 		name = no_name;
43502e23adSEtienne Carriere 
44502e23adSEtienne Carriere 	if (!is_unpaged((void *)name)) {
45502e23adSEtienne Carriere 		name = strdup(name);
46502e23adSEtienne Carriere 		if (!name)
47502e23adSEtienne Carriere 			panic();
48502e23adSEtienne Carriere 	}
49502e23adSEtienne Carriere 
50b7c94e43SEtienne Carriere 	ref = realloc(pm_cb_ref, sizeof(*ref) * (count + 1));
51b7c94e43SEtienne Carriere 	if (!ref)
52b7c94e43SEtienne Carriere 		panic();
53b7c94e43SEtienne Carriere 
54b7c94e43SEtienne Carriere 	ref[count] = *pm_hdl;
55b7c94e43SEtienne Carriere 	ref[count].flags = 0;
56502e23adSEtienne Carriere 	ref[count].name = name;
57b7c94e43SEtienne Carriere 
58b7c94e43SEtienne Carriere 	pm_cb_count = count + 1;
59b7c94e43SEtienne Carriere 	pm_cb_ref = ref;
60b7c94e43SEtienne Carriere }
61b7c94e43SEtienne Carriere 
62*5920ec25SEtienne Carriere static TEE_Result do_pm_callback(enum pm_op op, uint32_t pm_hint,
63*5920ec25SEtienne Carriere 				 struct pm_callback_handle *hdl)
64b7c94e43SEtienne Carriere {
65*5920ec25SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
66*5920ec25SEtienne Carriere 	bool suspending = op == PM_OP_SUSPEND;
67b7c94e43SEtienne Carriere 
68*5920ec25SEtienne Carriere 	if (suspending == (bool)(hdl->flags | PM_FLAG_SUSPENDED))
69*5920ec25SEtienne Carriere 		return TEE_SUCCESS;
70b7c94e43SEtienne Carriere 
71*5920ec25SEtienne Carriere 	DMSG("%s %s", suspending ? "Suspend" : "Resume", hdl->name);
72502e23adSEtienne Carriere 
73b7c94e43SEtienne Carriere 	res = hdl->callback(op, pm_hint, hdl);
74*5920ec25SEtienne Carriere 	if (res) {
75*5920ec25SEtienne Carriere 		EMSG("%s %s (%p) failed: %#"PRIx32, suspending ? "Suspend" :
76*5920ec25SEtienne Carriere 		     "Resume", hdl->name, (void *)(vaddr_t)hdl->callback, res);
77b7c94e43SEtienne Carriere 		return res;
78*5920ec25SEtienne Carriere 	}
79b7c94e43SEtienne Carriere 
80*5920ec25SEtienne Carriere 	if (suspending)
81b7c94e43SEtienne Carriere 		hdl->flags |= PM_FLAG_SUSPENDED;
82b7c94e43SEtienne Carriere 	else
83b7c94e43SEtienne Carriere 		hdl->flags &= ~PM_FLAG_SUSPENDED;
84*5920ec25SEtienne Carriere 
85*5920ec25SEtienne Carriere 	return TEE_SUCCESS;
86*5920ec25SEtienne Carriere }
87*5920ec25SEtienne Carriere 
88*5920ec25SEtienne Carriere static TEE_Result call_callbacks(enum pm_op op, uint32_t pm_hint,
89*5920ec25SEtienne Carriere 				 enum pm_callback_order order)
90*5920ec25SEtienne Carriere {
91*5920ec25SEtienne Carriere 	struct pm_callback_handle *hdl = NULL;
92*5920ec25SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
93*5920ec25SEtienne Carriere 	size_t n = 0;
94*5920ec25SEtienne Carriere 
95*5920ec25SEtienne Carriere 	/*
96*5920ec25SEtienne Carriere 	 * Suspend first the last registered instances.
97*5920ec25SEtienne Carriere 	 * Resume first the first registered instances.
98*5920ec25SEtienne Carriere 	 */
99*5920ec25SEtienne Carriere 	if (op == PM_OP_SUSPEND)
100*5920ec25SEtienne Carriere 		hdl = pm_cb_ref + pm_cb_count - 1;
101*5920ec25SEtienne Carriere 	else
102*5920ec25SEtienne Carriere 		hdl = pm_cb_ref;
103*5920ec25SEtienne Carriere 
104*5920ec25SEtienne Carriere 	for (n = 0; n < pm_cb_count; n++) {
105*5920ec25SEtienne Carriere 		if (hdl->order == order) {
106*5920ec25SEtienne Carriere 			res = do_pm_callback(op, pm_hint, hdl);
107*5920ec25SEtienne Carriere 			if (res)
108*5920ec25SEtienne Carriere 				return res;
109*5920ec25SEtienne Carriere 		}
110*5920ec25SEtienne Carriere 
111*5920ec25SEtienne Carriere 		if (op == PM_OP_SUSPEND)
112*5920ec25SEtienne Carriere 			hdl--;
113*5920ec25SEtienne Carriere 		else
114*5920ec25SEtienne Carriere 			hdl++;
115b7c94e43SEtienne Carriere 	}
116b7c94e43SEtienne Carriere 
117b7c94e43SEtienne Carriere 	return TEE_SUCCESS;
118b7c94e43SEtienne Carriere }
119b7c94e43SEtienne Carriere 
120b7c94e43SEtienne Carriere TEE_Result pm_change_state(enum pm_op op, uint32_t pm_hint)
121b7c94e43SEtienne Carriere {
122b7c94e43SEtienne Carriere 	enum pm_callback_order cnt;
123b7c94e43SEtienne Carriere 	TEE_Result res;
124b7c94e43SEtienne Carriere 
125b7c94e43SEtienne Carriere 	switch (op) {
126b7c94e43SEtienne Carriere 	case PM_OP_SUSPEND:
127b7c94e43SEtienne Carriere 		for (cnt = PM_CB_ORDER_DRIVER; cnt < PM_CB_ORDER_MAX; cnt++) {
128b7c94e43SEtienne Carriere 			res = call_callbacks(op, pm_hint, cnt);
129b7c94e43SEtienne Carriere 			if (res)
130b7c94e43SEtienne Carriere 				return res;
131b7c94e43SEtienne Carriere 		}
132b7c94e43SEtienne Carriere 		break;
133b7c94e43SEtienne Carriere 	case PM_OP_RESUME:
134b7c94e43SEtienne Carriere 		for (cnt = PM_CB_ORDER_MAX; cnt > PM_CB_ORDER_DRIVER; cnt--) {
135b7c94e43SEtienne Carriere 			res = call_callbacks(op, pm_hint, cnt - 1);
136b7c94e43SEtienne Carriere 			if (res)
137b7c94e43SEtienne Carriere 				return res;
138b7c94e43SEtienne Carriere 		}
139b7c94e43SEtienne Carriere 		break;
140b7c94e43SEtienne Carriere 	default:
141b7c94e43SEtienne Carriere 		panic();
142b7c94e43SEtienne Carriere 	}
143b7c94e43SEtienne Carriere 
144b7c94e43SEtienne Carriere 	return TEE_SUCCESS;
145b7c94e43SEtienne Carriere }
146