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> 95920ec25SEtienne 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 625920ec25SEtienne Carriere static TEE_Result do_pm_callback(enum pm_op op, uint32_t pm_hint, 635920ec25SEtienne Carriere struct pm_callback_handle *hdl) 64b7c94e43SEtienne Carriere { 655920ec25SEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 665920ec25SEtienne Carriere bool suspending = op == PM_OP_SUSPEND; 67b7c94e43SEtienne Carriere 685920ec25SEtienne Carriere if (suspending == (bool)(hdl->flags | PM_FLAG_SUSPENDED)) 695920ec25SEtienne Carriere return TEE_SUCCESS; 70b7c94e43SEtienne Carriere 715920ec25SEtienne Carriere DMSG("%s %s", suspending ? "Suspend" : "Resume", hdl->name); 72502e23adSEtienne Carriere 73b7c94e43SEtienne Carriere res = hdl->callback(op, pm_hint, hdl); 745920ec25SEtienne Carriere if (res) { 755920ec25SEtienne Carriere EMSG("%s %s (%p) failed: %#"PRIx32, suspending ? "Suspend" : 765920ec25SEtienne Carriere "Resume", hdl->name, (void *)(vaddr_t)hdl->callback, res); 77b7c94e43SEtienne Carriere return res; 785920ec25SEtienne Carriere } 79b7c94e43SEtienne Carriere 805920ec25SEtienne Carriere if (suspending) 81b7c94e43SEtienne Carriere hdl->flags |= PM_FLAG_SUSPENDED; 82b7c94e43SEtienne Carriere else 83b7c94e43SEtienne Carriere hdl->flags &= ~PM_FLAG_SUSPENDED; 845920ec25SEtienne Carriere 855920ec25SEtienne Carriere return TEE_SUCCESS; 865920ec25SEtienne Carriere } 875920ec25SEtienne Carriere 885920ec25SEtienne Carriere static TEE_Result call_callbacks(enum pm_op op, uint32_t pm_hint, 895920ec25SEtienne Carriere enum pm_callback_order order) 905920ec25SEtienne Carriere { 915920ec25SEtienne Carriere struct pm_callback_handle *hdl = NULL; 925920ec25SEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 935920ec25SEtienne Carriere size_t n = 0; 945920ec25SEtienne Carriere 955920ec25SEtienne Carriere /* 965920ec25SEtienne Carriere * Suspend first the last registered instances. 975920ec25SEtienne Carriere * Resume first the first registered instances. 985920ec25SEtienne Carriere */ 995920ec25SEtienne Carriere if (op == PM_OP_SUSPEND) 1005920ec25SEtienne Carriere hdl = pm_cb_ref + pm_cb_count - 1; 1015920ec25SEtienne Carriere else 1025920ec25SEtienne Carriere hdl = pm_cb_ref; 1035920ec25SEtienne Carriere 1045920ec25SEtienne Carriere for (n = 0; n < pm_cb_count; n++) { 1055920ec25SEtienne Carriere if (hdl->order == order) { 1065920ec25SEtienne Carriere res = do_pm_callback(op, pm_hint, hdl); 1075920ec25SEtienne Carriere if (res) 1085920ec25SEtienne Carriere return res; 1095920ec25SEtienne Carriere } 1105920ec25SEtienne Carriere 1115920ec25SEtienne Carriere if (op == PM_OP_SUSPEND) 1125920ec25SEtienne Carriere hdl--; 1135920ec25SEtienne Carriere else 1145920ec25SEtienne 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 { 122*36ebac6dSEtienne Carriere enum pm_callback_order cnt = PM_CB_ORDER_DRIVER; 123*36ebac6dSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 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