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