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