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