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> 10ffc1ebb2SMarouene 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 19*502e23adSEtienne Carriere static const char no_name[] = "no-name"; 20*502e23adSEtienne Carriere DECLARE_KEEP_PAGER(no_name); 21*502e23adSEtienne 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 { 35*502e23adSEtienne Carriere struct pm_callback_handle *ref = NULL; 36*502e23adSEtienne 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 41*502e23adSEtienne Carriere if (!name) 42*502e23adSEtienne Carriere name = no_name; 43*502e23adSEtienne Carriere 44*502e23adSEtienne Carriere if (!is_unpaged((void *)name)) { 45*502e23adSEtienne Carriere name = strdup(name); 46*502e23adSEtienne Carriere if (!name) 47*502e23adSEtienne Carriere panic(); 48*502e23adSEtienne Carriere } 49*502e23adSEtienne 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; 56*502e23adSEtienne Carriere ref[count].name = name; 57b7c94e43SEtienne Carriere 58b7c94e43SEtienne Carriere pm_cb_count = count + 1; 59b7c94e43SEtienne Carriere pm_cb_ref = ref; 60b7c94e43SEtienne Carriere } 61b7c94e43SEtienne Carriere 62b7c94e43SEtienne Carriere static TEE_Result call_callbacks(enum pm_op op, uint32_t pm_hint, 63b7c94e43SEtienne Carriere enum pm_callback_order order) 64b7c94e43SEtienne Carriere { 65b7c94e43SEtienne Carriere struct pm_callback_handle *hdl; 66b7c94e43SEtienne Carriere size_t n; 67b7c94e43SEtienne Carriere TEE_Result res; 68b7c94e43SEtienne Carriere 69b7c94e43SEtienne Carriere for (n = 0, hdl = pm_cb_ref; n < pm_cb_count; n++, hdl++) { 70b7c94e43SEtienne Carriere if (hdl->order != order || 71b7c94e43SEtienne Carriere (hdl->flags & PM_FLAG_SUSPENDED) == (op == PM_OP_SUSPEND)) 72b7c94e43SEtienne Carriere continue; 73b7c94e43SEtienne Carriere 74*502e23adSEtienne Carriere DMSG("%s %s (%p)", op == PM_OP_SUSPEND ? "Suspend" : "Resume", 75*502e23adSEtienne Carriere hdl->name, (void *)hdl->callback); 76*502e23adSEtienne Carriere 77b7c94e43SEtienne Carriere res = hdl->callback(op, pm_hint, hdl); 78b7c94e43SEtienne Carriere if (res) 79b7c94e43SEtienne Carriere return res; 80b7c94e43SEtienne Carriere 81b7c94e43SEtienne Carriere if (op == PM_OP_SUSPEND) 82b7c94e43SEtienne Carriere hdl->flags |= PM_FLAG_SUSPENDED; 83b7c94e43SEtienne Carriere else 84b7c94e43SEtienne Carriere hdl->flags &= ~PM_FLAG_SUSPENDED; 85b7c94e43SEtienne Carriere } 86b7c94e43SEtienne Carriere 87b7c94e43SEtienne Carriere return TEE_SUCCESS; 88b7c94e43SEtienne Carriere } 89b7c94e43SEtienne Carriere 90b7c94e43SEtienne Carriere TEE_Result pm_change_state(enum pm_op op, uint32_t pm_hint) 91b7c94e43SEtienne Carriere { 92b7c94e43SEtienne Carriere enum pm_callback_order cnt; 93b7c94e43SEtienne Carriere TEE_Result res; 94b7c94e43SEtienne Carriere 95b7c94e43SEtienne Carriere switch (op) { 96b7c94e43SEtienne Carriere case PM_OP_SUSPEND: 97b7c94e43SEtienne Carriere for (cnt = PM_CB_ORDER_DRIVER; cnt < PM_CB_ORDER_MAX; cnt++) { 98b7c94e43SEtienne Carriere res = call_callbacks(op, pm_hint, cnt); 99b7c94e43SEtienne Carriere if (res) 100b7c94e43SEtienne Carriere return res; 101b7c94e43SEtienne Carriere } 102b7c94e43SEtienne Carriere break; 103b7c94e43SEtienne Carriere case PM_OP_RESUME: 104b7c94e43SEtienne Carriere for (cnt = PM_CB_ORDER_MAX; cnt > PM_CB_ORDER_DRIVER; cnt--) { 105b7c94e43SEtienne Carriere res = call_callbacks(op, pm_hint, cnt - 1); 106b7c94e43SEtienne Carriere if (res) 107b7c94e43SEtienne Carriere return res; 108b7c94e43SEtienne Carriere } 109b7c94e43SEtienne Carriere break; 110b7c94e43SEtienne Carriere default: 111b7c94e43SEtienne Carriere panic(); 112b7c94e43SEtienne Carriere } 113b7c94e43SEtienne Carriere 114b7c94e43SEtienne Carriere return TEE_SUCCESS; 115b7c94e43SEtienne Carriere } 116