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