GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
+589
@@ -0,0 +1,589 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved.
|
||||
* Copyright (c) 2022, Advanced Micro Devices, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* Versal system level PM-API functions and communication with PMC via
|
||||
* IPI interrupts
|
||||
*/
|
||||
|
||||
#include <pm_common.h>
|
||||
#include <pm_ipi.h>
|
||||
#include <plat/common/platform.h>
|
||||
#include "pm_api_sys.h"
|
||||
#include "pm_client.h"
|
||||
#include "pm_defs.h"
|
||||
#include "pm_svc_main.h"
|
||||
|
||||
/* default shutdown/reboot scope is system(2) */
|
||||
static uint32_t pm_shutdown_scope = XPM_SHUTDOWN_SUBTYPE_RST_SYSTEM;
|
||||
|
||||
/**
|
||||
* pm_get_shutdown_scope() - Get the currently set shutdown scope
|
||||
*
|
||||
* @return Shutdown scope value
|
||||
*/
|
||||
uint32_t pm_get_shutdown_scope(void)
|
||||
{
|
||||
return pm_shutdown_scope;
|
||||
}
|
||||
|
||||
/* PM API functions */
|
||||
|
||||
/**
|
||||
* pm_handle_eemi_call() - PM call for processor to send eemi payload
|
||||
* @flag 0 - Call from secure source
|
||||
* 1 - Call from non-secure source
|
||||
* @x0 to x5 Arguments received per SMC64 standard
|
||||
* @result Payload received from firmware
|
||||
*
|
||||
* @return PM_RET_SUCCESS on success or error code
|
||||
*/
|
||||
enum pm_ret_status pm_handle_eemi_call(uint32_t flag, uint32_t x0, uint32_t x1,
|
||||
uint32_t x2, uint32_t x3, uint32_t x4,
|
||||
uint32_t x5, uint64_t *result)
|
||||
{
|
||||
uint32_t payload[PAYLOAD_ARG_CNT] = {0};
|
||||
uint32_t module_id;
|
||||
|
||||
module_id = (x0 & MODULE_ID_MASK) >> 8U;
|
||||
|
||||
//default module id is for LIBPM
|
||||
if (module_id == 0) {
|
||||
module_id = LIBPM_MODULE_ID;
|
||||
}
|
||||
|
||||
PM_PACK_PAYLOAD6(payload, module_id, flag, x0, x1, x2, x3, x4, x5);
|
||||
return pm_ipi_send_sync(primary_proc, payload, (uint32_t *)result, PAYLOAD_ARG_CNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_self_suspend() - PM call for processor to suspend itself
|
||||
* @nid Node id of the processor or subsystem
|
||||
* @latency Requested maximum wakeup latency (not supported)
|
||||
* @state Requested state
|
||||
* @address Resume address
|
||||
* @flag 0 - Call from secure source
|
||||
* 1 - Call from non-secure source
|
||||
*
|
||||
* This is a blocking call, it will return only once PMU has responded.
|
||||
* On a wakeup, resume address will be automatically set by PMU.
|
||||
*
|
||||
* @return Returns status, either success or error+reason
|
||||
*/
|
||||
enum pm_ret_status pm_self_suspend(uint32_t nid,
|
||||
uint32_t latency,
|
||||
uint32_t state,
|
||||
uintptr_t address, uint32_t flag)
|
||||
{
|
||||
uint32_t payload[PAYLOAD_ARG_CNT];
|
||||
uint32_t cpuid = plat_my_core_pos();
|
||||
const struct pm_proc *proc = pm_get_proc(cpuid);
|
||||
|
||||
if (proc == NULL) {
|
||||
WARN("Failed to get proc %d\n", cpuid);
|
||||
return PM_RET_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do client specific suspend operations
|
||||
* (e.g. set powerdown request bit)
|
||||
*/
|
||||
pm_client_suspend(proc, state);
|
||||
|
||||
/* Send request to the PLM */
|
||||
PM_PACK_PAYLOAD6(payload, LIBPM_MODULE_ID, flag, PM_SELF_SUSPEND,
|
||||
proc->node_id, latency, state, address,
|
||||
(address >> 32));
|
||||
return pm_ipi_send_sync(proc, payload, NULL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_abort_suspend() - PM call to announce that a prior suspend request
|
||||
* is to be aborted.
|
||||
* @reason Reason for the abort
|
||||
* @flag 0 - Call from secure source
|
||||
* 1 - Call from non-secure source
|
||||
*
|
||||
* Calling PU expects the PMU to abort the initiated suspend procedure.
|
||||
* This is a non-blocking call without any acknowledge.
|
||||
*
|
||||
* @return Returns status, either success or error+reason
|
||||
*/
|
||||
enum pm_ret_status pm_abort_suspend(enum pm_abort_reason reason, uint32_t flag)
|
||||
{
|
||||
uint32_t payload[PAYLOAD_ARG_CNT];
|
||||
|
||||
/*
|
||||
* Do client specific abort suspend operations
|
||||
* (e.g. enable interrupts and clear powerdown request bit)
|
||||
*/
|
||||
pm_client_abort_suspend();
|
||||
|
||||
/* Send request to the PLM */
|
||||
PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, flag, PM_ABORT_SUSPEND,
|
||||
reason, primary_proc->node_id);
|
||||
return pm_ipi_send_sync(primary_proc, payload, NULL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_req_suspend() - PM call to request for another PU or subsystem to
|
||||
* be suspended gracefully.
|
||||
* @target Node id of the targeted PU or subsystem
|
||||
* @ack Flag to specify whether acknowledge is requested
|
||||
* @latency Requested wakeup latency (not supported)
|
||||
* @state Requested state (not supported)
|
||||
* @flag 0 - Call from secure source
|
||||
* 1 - Call from non-secure source
|
||||
*
|
||||
* @return Returns status, either success or error+reason
|
||||
*/
|
||||
enum pm_ret_status pm_req_suspend(uint32_t target, uint8_t ack,
|
||||
uint32_t latency, uint32_t state,
|
||||
uint32_t flag)
|
||||
{
|
||||
uint32_t payload[PAYLOAD_ARG_CNT];
|
||||
|
||||
/* Send request to the PMU */
|
||||
PM_PACK_PAYLOAD4(payload, LIBPM_MODULE_ID, flag, PM_REQ_SUSPEND, target,
|
||||
latency, state);
|
||||
if (ack == IPI_BLOCKING) {
|
||||
return pm_ipi_send_sync(primary_proc, payload, NULL, 0);
|
||||
} else {
|
||||
return pm_ipi_send(primary_proc, payload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_req_wakeup() - PM call for processor to wake up selected processor
|
||||
* or subsystem
|
||||
* @target Device ID of the processor or subsystem to wake up
|
||||
* @set_address Resume address presence indicator
|
||||
* 1 - resume address specified, 0 - otherwise
|
||||
* @address Resume address
|
||||
* @ack Flag to specify whether acknowledge requested
|
||||
* @flag 0 - Call from secure source
|
||||
* 1 - Call from non-secure source
|
||||
*
|
||||
* This API function is either used to power up another APU core for SMP
|
||||
* (by PSCI) or to power up an entirely different PU or subsystem, such
|
||||
* as RPU0, RPU, or PL_CORE_xx. Resume address for the target PU will be
|
||||
* automatically set by PMC.
|
||||
*
|
||||
* @return Returns status, either success or error+reason
|
||||
*/
|
||||
enum pm_ret_status pm_req_wakeup(uint32_t target, uint32_t set_address,
|
||||
uintptr_t address, uint8_t ack, uint32_t flag)
|
||||
{
|
||||
uint32_t payload[PAYLOAD_ARG_CNT];
|
||||
|
||||
/* Send request to the PMC to perform the wake of the PU */
|
||||
PM_PACK_PAYLOAD5(payload, LIBPM_MODULE_ID, flag, PM_REQ_WAKEUP, target,
|
||||
set_address, address, ack);
|
||||
|
||||
return pm_ipi_send_sync(primary_proc, payload, NULL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_get_callbackdata() - Read from IPI response buffer
|
||||
* @data - array of PAYLOAD_ARG_CNT elements
|
||||
* @flag - 0 - Call from secure source
|
||||
* 1 - Call from non-secure source
|
||||
* @ack - 0 - Do not ack IPI after reading payload
|
||||
* 1 - Ack IPI after reading payload
|
||||
*
|
||||
* Read value from ipi buffer response buffer.
|
||||
*/
|
||||
void pm_get_callbackdata(uint32_t *data, size_t count, uint32_t flag, uint32_t ack)
|
||||
{
|
||||
/* Return if interrupt is not from PMU */
|
||||
if (pm_ipi_irq_status(primary_proc) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
pm_ipi_buff_read_callb(data, count);
|
||||
|
||||
if (ack != 0U) {
|
||||
pm_ipi_irq_clear(primary_proc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_pll_set_param() - Set PLL parameter
|
||||
*
|
||||
* This API is deprecated and maintained here for backward compatibility.
|
||||
* New use of this API should be avoided for versal platform.
|
||||
* This API and its use cases will be removed for versal platform.
|
||||
*
|
||||
* @clk_id PLL clock ID
|
||||
* @param PLL parameter ID
|
||||
* @value Value to set for PLL parameter
|
||||
* @flag 0 - Call from secure source
|
||||
* 1 - Call from non-secure source
|
||||
*
|
||||
* @return Returns status, either success or error+reason
|
||||
*/
|
||||
enum pm_ret_status pm_pll_set_param(uint32_t clk_id, uint32_t param,
|
||||
uint32_t value, uint32_t flag)
|
||||
{
|
||||
uint32_t payload[PAYLOAD_ARG_CNT];
|
||||
|
||||
/* Send request to the PMC */
|
||||
PM_PACK_PAYLOAD4(payload, LIBPM_MODULE_ID, flag, PM_PLL_SET_PARAMETER,
|
||||
clk_id, param, value);
|
||||
|
||||
return pm_ipi_send_sync(primary_proc, payload, NULL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_pll_get_param() - Get PLL parameter value
|
||||
*
|
||||
* This API is deprecated and maintained here for backward compatibility.
|
||||
* New use of this API should be avoided for versal platform.
|
||||
* This API and its use cases will be removed for versal platform.
|
||||
*
|
||||
* @clk_id PLL clock ID
|
||||
* @param PLL parameter ID
|
||||
* @value: Buffer to store PLL parameter value
|
||||
* @flag 0 - Call from secure source
|
||||
* 1 - Call from non-secure source
|
||||
*
|
||||
* @return Returns status, either success or error+reason
|
||||
*/
|
||||
enum pm_ret_status pm_pll_get_param(uint32_t clk_id, uint32_t param,
|
||||
uint32_t *value, uint32_t flag)
|
||||
{
|
||||
uint32_t payload[PAYLOAD_ARG_CNT];
|
||||
|
||||
/* Send request to the PMC */
|
||||
PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, flag, PM_PLL_GET_PARAMETER,
|
||||
clk_id, param);
|
||||
|
||||
return pm_ipi_send_sync(primary_proc, payload, value, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_pll_set_mode() - Set PLL mode
|
||||
*
|
||||
* This API is deprecated and maintained here for backward compatibility.
|
||||
* New use of this API should be avoided for versal platform.
|
||||
* This API and its use cases will be removed for versal platform.
|
||||
*
|
||||
* @clk_id PLL clock ID
|
||||
* @mode PLL mode
|
||||
* @flag 0 - Call from secure source
|
||||
* 1 - Call from non-secure source
|
||||
*
|
||||
* @return Returns status, either success or error+reason
|
||||
*/
|
||||
enum pm_ret_status pm_pll_set_mode(uint32_t clk_id, uint32_t mode,
|
||||
uint32_t flag)
|
||||
{
|
||||
uint32_t payload[PAYLOAD_ARG_CNT];
|
||||
|
||||
/* Send request to the PMC */
|
||||
PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, flag, PM_PLL_SET_MODE,
|
||||
clk_id, mode);
|
||||
|
||||
return pm_ipi_send_sync(primary_proc, payload, NULL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_pll_get_mode() - Get PLL mode
|
||||
*
|
||||
* This API is deprecated and maintained here for backward compatibility.
|
||||
* New use of this API should be avoided for versal platform.
|
||||
* This API and its use cases will be removed for versal platform.
|
||||
*
|
||||
* @clk_id PLL clock ID
|
||||
* @mode: Buffer to store PLL mode
|
||||
* @flag 0 - Call from secure source
|
||||
* 1 - Call from non-secure source
|
||||
*
|
||||
* @return Returns status, either success or error+reason
|
||||
*/
|
||||
enum pm_ret_status pm_pll_get_mode(uint32_t clk_id, uint32_t *mode,
|
||||
uint32_t flag)
|
||||
{
|
||||
uint32_t payload[PAYLOAD_ARG_CNT];
|
||||
|
||||
/* Send request to the PMC */
|
||||
PM_PACK_PAYLOAD2(payload, LIBPM_MODULE_ID, flag, PM_PLL_GET_MODE,
|
||||
clk_id);
|
||||
|
||||
return pm_ipi_send_sync(primary_proc, payload, mode, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_force_powerdown() - PM call to request for another PU or subsystem to
|
||||
* be powered down forcefully
|
||||
* @target Device ID of the PU node to be forced powered down.
|
||||
* @ack Flag to specify whether acknowledge is requested
|
||||
* @flag 0 - Call from secure source
|
||||
* 1 - Call from non-secure source
|
||||
*
|
||||
* @return Returns status, either success or error+reason
|
||||
*/
|
||||
enum pm_ret_status pm_force_powerdown(uint32_t target, uint8_t ack,
|
||||
uint32_t flag)
|
||||
{
|
||||
uint32_t payload[PAYLOAD_ARG_CNT];
|
||||
|
||||
/* Send request to the PMC */
|
||||
PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, flag, PM_FORCE_POWERDOWN,
|
||||
target, ack);
|
||||
|
||||
if (ack == IPI_BLOCKING) {
|
||||
return pm_ipi_send_sync(primary_proc, payload, NULL, 0);
|
||||
} else {
|
||||
return pm_ipi_send(primary_proc, payload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_system_shutdown() - PM call to request a system shutdown or restart
|
||||
* @type Shutdown or restart? 0=shutdown, 1=restart, 2=setscope
|
||||
* @subtype Scope: 0=APU-subsystem, 1=PS, 2=system
|
||||
* @flag 0 - Call from secure source
|
||||
* 1 - Call from non-secure source
|
||||
*
|
||||
* @return Returns status, either success or error+reason
|
||||
*/
|
||||
enum pm_ret_status pm_system_shutdown(uint32_t type, uint32_t subtype,
|
||||
uint32_t flag)
|
||||
{
|
||||
uint32_t payload[PAYLOAD_ARG_CNT];
|
||||
|
||||
if (type == XPM_SHUTDOWN_TYPE_SETSCOPE_ONLY) {
|
||||
/* Setting scope for subsequent PSCI reboot or shutdown */
|
||||
pm_shutdown_scope = subtype;
|
||||
return PM_RET_SUCCESS;
|
||||
}
|
||||
|
||||
/* Send request to the PMC */
|
||||
PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, flag, PM_SYSTEM_SHUTDOWN,
|
||||
type, subtype);
|
||||
|
||||
return pm_ipi_send_non_blocking(primary_proc, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_query_data() - PM API for querying firmware data
|
||||
*
|
||||
* This API is deprecated and maintained here for backward compatibility.
|
||||
* New use of this API should be avoided for versal platform.
|
||||
* This API and its use cases will be removed for versal platform.
|
||||
*
|
||||
* @qid The type of data to query
|
||||
* @arg1 Argument 1 to requested query data call
|
||||
* @arg2 Argument 2 to requested query data call
|
||||
* @arg3 Argument 3 to requested query data call
|
||||
* @data Returned output data
|
||||
* @flag 0 - Call from secure source
|
||||
* 1 - Call from non-secure source
|
||||
*
|
||||
* @retur - 0 if success else non-zero error code of type
|
||||
* enum pm_ret_status
|
||||
*/
|
||||
enum pm_ret_status pm_query_data(uint32_t qid, uint32_t arg1, uint32_t arg2,
|
||||
uint32_t arg3, uint32_t *data, uint32_t flag)
|
||||
{
|
||||
uint32_t ret;
|
||||
uint32_t version[PAYLOAD_ARG_CNT] = {0};
|
||||
uint32_t payload[PAYLOAD_ARG_CNT];
|
||||
uint32_t fw_api_version;
|
||||
|
||||
/* Send request to the PMC */
|
||||
PM_PACK_PAYLOAD5(payload, LIBPM_MODULE_ID, flag, PM_QUERY_DATA, qid,
|
||||
arg1, arg2, arg3);
|
||||
|
||||
ret = pm_feature_check(PM_QUERY_DATA, &version[0], flag);
|
||||
if (ret == PM_RET_SUCCESS) {
|
||||
fw_api_version = version[0] & 0xFFFFU;
|
||||
if ((fw_api_version == 2U) &&
|
||||
((qid == XPM_QID_CLOCK_GET_NAME) ||
|
||||
(qid == XPM_QID_PINCTRL_GET_FUNCTION_NAME))) {
|
||||
ret = pm_ipi_send_sync(primary_proc, payload, data, PAYLOAD_ARG_CNT);
|
||||
if (ret == PM_RET_SUCCESS) {
|
||||
ret = data[0];
|
||||
data[0] = data[1];
|
||||
data[1] = data[2];
|
||||
data[2] = data[3];
|
||||
}
|
||||
} else {
|
||||
ret = pm_ipi_send_sync(primary_proc, payload, data, PAYLOAD_ARG_CNT);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* pm_api_ioctl() - PM IOCTL API for device control and configs
|
||||
*
|
||||
* This API is deprecated and maintained here for backward compatibility.
|
||||
* New use of this API should be avoided for versal platform.
|
||||
* This API and its use cases will be removed for versal platform.
|
||||
*
|
||||
* @device_id Device ID
|
||||
* @ioctl_id ID of the requested IOCTL
|
||||
* @arg1 Argument 1 to requested IOCTL call
|
||||
* @arg2 Argument 2 to requested IOCTL call
|
||||
* @arg3 Argument 3 to requested IOCTL call
|
||||
* @value Returned output value
|
||||
* @flag 0 - Call from secure source
|
||||
* 1 - Call from non-secure source
|
||||
*
|
||||
* This function calls IOCTL to firmware for device control and configuration.
|
||||
*
|
||||
* @return Returns status, either 0 on success or non-zero error code
|
||||
* of type enum pm_ret_status
|
||||
*/
|
||||
enum pm_ret_status pm_api_ioctl(uint32_t device_id, uint32_t ioctl_id,
|
||||
uint32_t arg1, uint32_t arg2, uint32_t arg3,
|
||||
uint32_t *value, uint32_t flag)
|
||||
{
|
||||
enum pm_ret_status ret;
|
||||
|
||||
switch (ioctl_id) {
|
||||
case IOCTL_SET_PLL_FRAC_MODE:
|
||||
ret = pm_pll_set_mode(arg1, arg2, flag);
|
||||
break;
|
||||
case IOCTL_GET_PLL_FRAC_MODE:
|
||||
ret = pm_pll_get_mode(arg1, value, flag);
|
||||
break;
|
||||
case IOCTL_SET_PLL_FRAC_DATA:
|
||||
ret = pm_pll_set_param(arg1, PM_PLL_PARAM_DATA, arg2, flag);
|
||||
break;
|
||||
case IOCTL_GET_PLL_FRAC_DATA:
|
||||
ret = pm_pll_get_param(arg1, PM_PLL_PARAM_DATA, value, flag);
|
||||
break;
|
||||
case IOCTL_SET_SGI:
|
||||
/* Get the sgi number */
|
||||
ret = pm_register_sgi(arg1, arg2);
|
||||
if (ret != 0) {
|
||||
return PM_RET_ERROR_ARGS;
|
||||
}
|
||||
ret = PM_RET_SUCCESS;
|
||||
break;
|
||||
default:
|
||||
return PM_RET_ERROR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_set_wakeup_source() - PM call to specify the wakeup source while suspended
|
||||
* @target Device id of the targeted PU or subsystem
|
||||
* @wkup_node Device id of the wakeup peripheral
|
||||
* @enable Enable or disable the specified peripheral as wake source
|
||||
* @flag 0 - Call from secure source
|
||||
* 1 - Call from non-secure source
|
||||
*
|
||||
* @return Returns status, either success or error+reason
|
||||
*/
|
||||
enum pm_ret_status pm_set_wakeup_source(uint32_t target, uint32_t wkup_device,
|
||||
uint8_t enable, uint32_t flag)
|
||||
{
|
||||
uint32_t payload[PAYLOAD_ARG_CNT];
|
||||
|
||||
PM_PACK_PAYLOAD4(payload, LIBPM_MODULE_ID, flag, PM_SET_WAKEUP_SOURCE,
|
||||
target, wkup_device, enable);
|
||||
return pm_ipi_send_sync(primary_proc, payload, NULL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_feature_check() - Returns the supported API version if supported
|
||||
* @api_id API ID to check
|
||||
* @flag 0 - Call from secure source
|
||||
* 1 - Call from non-secure source
|
||||
* @ret_payload pointer to array of PAYLOAD_ARG_CNT number of
|
||||
* words Returned supported API version and bitmasks
|
||||
* for IOCTL and QUERY ID
|
||||
*
|
||||
* @return Returns status, either success or error+reason
|
||||
*/
|
||||
enum pm_ret_status pm_feature_check(uint32_t api_id, uint32_t *ret_payload,
|
||||
uint32_t flag)
|
||||
{
|
||||
uint32_t payload[PAYLOAD_ARG_CNT];
|
||||
uint32_t module_id;
|
||||
|
||||
/* Return version of API which are implemented in ATF only */
|
||||
switch (api_id) {
|
||||
case PM_GET_CALLBACK_DATA:
|
||||
case PM_GET_TRUSTZONE_VERSION:
|
||||
ret_payload[0] = PM_API_VERSION_2;
|
||||
return PM_RET_SUCCESS;
|
||||
case TF_A_PM_REGISTER_SGI:
|
||||
ret_payload[0] = PM_API_BASE_VERSION;
|
||||
return PM_RET_SUCCESS;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
module_id = (api_id & MODULE_ID_MASK) >> 8U;
|
||||
|
||||
/*
|
||||
* feature check should be done only for LIBPM module
|
||||
* If module_id is 0, then we consider it LIBPM module as default id
|
||||
*/
|
||||
if ((module_id > 0) && (module_id != LIBPM_MODULE_ID)) {
|
||||
return PM_RET_SUCCESS;
|
||||
}
|
||||
|
||||
PM_PACK_PAYLOAD2(payload, LIBPM_MODULE_ID, flag,
|
||||
PM_FEATURE_CHECK, api_id);
|
||||
return pm_ipi_send_sync(primary_proc, payload, ret_payload, PAYLOAD_ARG_CNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_load_pdi() - Load the PDI
|
||||
*
|
||||
* This function provides support to load PDI from linux
|
||||
*
|
||||
* src: Source device of pdi(DDR, OCM, SD etc)
|
||||
* address_low: lower 32-bit Linear memory space address
|
||||
* address_high: higher 32-bit Linear memory space address
|
||||
* @flag 0 - Call from secure source
|
||||
* 1 - Call from non-secure source
|
||||
*
|
||||
* @return Returns status, either success or error+reason
|
||||
*/
|
||||
enum pm_ret_status pm_load_pdi(uint32_t src, uint32_t address_low,
|
||||
uint32_t address_high, uint32_t flag)
|
||||
{
|
||||
uint32_t payload[PAYLOAD_ARG_CNT];
|
||||
|
||||
/* Send request to the PMU */
|
||||
PM_PACK_PAYLOAD4(payload, LOADER_MODULE_ID, flag, PM_LOAD_PDI, src,
|
||||
address_high, address_low);
|
||||
return pm_ipi_send_sync(primary_proc, payload, NULL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_register_notifier() - PM call to register a subsystem to be notified
|
||||
* about the device event
|
||||
* @device_id Device ID for the Node to which the event is related
|
||||
* @event Event in question
|
||||
* @wake Wake subsystem upon capturing the event if value 1
|
||||
* @enable Enable the registration for value 1, disable for value 0
|
||||
* @flag 0 - Call from secure source
|
||||
* 1 - Call from non-secure source
|
||||
*
|
||||
* @return Returns status, either success or error+reason
|
||||
*/
|
||||
enum pm_ret_status pm_register_notifier(uint32_t device_id, uint32_t event,
|
||||
uint32_t wake, uint32_t enable,
|
||||
uint32_t flag)
|
||||
{
|
||||
uint32_t payload[PAYLOAD_ARG_CNT];
|
||||
|
||||
/* Send request to the PMC */
|
||||
PM_PACK_PAYLOAD5(payload, LIBPM_MODULE_ID, flag, PM_REGISTER_NOTIFIER,
|
||||
device_id, event, wake, enable);
|
||||
|
||||
return pm_ipi_send_sync(primary_proc, payload, NULL, 0);
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved.
|
||||
* Copyright (c) 2022, Advanced Micro Devices, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef PM_API_SYS_H
|
||||
#define PM_API_SYS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "pm_defs.h"
|
||||
|
||||
/*********************************************************************
|
||||
* Target module IDs macros
|
||||
********************************************************************/
|
||||
#define LIBPM_MODULE_ID 0x2U
|
||||
#define LOADER_MODULE_ID 0x7U
|
||||
|
||||
#define MODULE_ID_MASK 0x0000ff00U
|
||||
/**********************************************************
|
||||
* PM API function declarations
|
||||
**********************************************************/
|
||||
|
||||
enum pm_ret_status pm_handle_eemi_call(uint32_t flag, uint32_t x0, uint32_t x1,
|
||||
uint32_t x2, uint32_t x3, uint32_t x4,
|
||||
uint32_t x5, uint64_t *result);
|
||||
enum pm_ret_status pm_self_suspend(uint32_t nid,
|
||||
uint32_t latency,
|
||||
uint32_t state,
|
||||
uintptr_t address, uint32_t flag);
|
||||
enum pm_ret_status pm_abort_suspend(enum pm_abort_reason reason, uint32_t flag);
|
||||
enum pm_ret_status pm_req_suspend(uint32_t target,
|
||||
uint8_t ack,
|
||||
uint32_t latency,
|
||||
uint32_t state, uint32_t flag);
|
||||
enum pm_ret_status pm_req_wakeup(uint32_t target, uint32_t set_address,
|
||||
uintptr_t address, uint8_t ack, uint32_t flag);
|
||||
enum pm_ret_status pm_set_wakeup_source(uint32_t target, uint32_t device_id,
|
||||
uint8_t enable, uint32_t flag);
|
||||
void pm_get_callbackdata(uint32_t *data, size_t count, uint32_t flag,
|
||||
uint32_t ack);
|
||||
enum pm_ret_status pm_pll_set_param(uint32_t clk_id, uint32_t param,
|
||||
uint32_t value, uint32_t flag);
|
||||
enum pm_ret_status pm_pll_get_param(uint32_t clk_id, uint32_t param,
|
||||
uint32_t *value, uint32_t flag);
|
||||
enum pm_ret_status pm_pll_set_mode(uint32_t clk_id, uint32_t mode,
|
||||
uint32_t flag);
|
||||
enum pm_ret_status pm_pll_get_mode(uint32_t clk_id, uint32_t *mode,
|
||||
uint32_t flag);
|
||||
enum pm_ret_status pm_force_powerdown(uint32_t target, uint8_t ack,
|
||||
uint32_t flag);
|
||||
enum pm_ret_status pm_system_shutdown(uint32_t type, uint32_t subtype,
|
||||
uint32_t flag);
|
||||
enum pm_ret_status pm_api_ioctl(uint32_t device_id, uint32_t ioctl_id,
|
||||
uint32_t arg1, uint32_t arg2, uint32_t arg3,
|
||||
uint32_t *value, uint32_t flag);
|
||||
enum pm_ret_status pm_query_data(uint32_t qid, uint32_t arg1, uint32_t arg2,
|
||||
uint32_t arg3, uint32_t *data, uint32_t flag);
|
||||
uint32_t pm_get_shutdown_scope(void);
|
||||
enum pm_ret_status pm_feature_check(uint32_t api_id, uint32_t *ret_payload,
|
||||
uint32_t flag);
|
||||
enum pm_ret_status pm_load_pdi(uint32_t src, uint32_t address_low,
|
||||
uint32_t address_high, uint32_t flag);
|
||||
enum pm_ret_status pm_register_notifier(uint32_t device_id, uint32_t event,
|
||||
uint32_t wake, uint32_t enable,
|
||||
uint32_t flag);
|
||||
|
||||
/**
|
||||
* Assigning of argument values into array elements.
|
||||
*/
|
||||
#define PM_PACK_PAYLOAD1(pl, mid, flag, arg0) { \
|
||||
pl[0] = (uint32_t)(((uint32_t)(arg0) & 0xFFU) | ((mid) << 8U) | ((flag) << 24U)); \
|
||||
}
|
||||
|
||||
#define PM_PACK_PAYLOAD2(pl, mid, flag, arg0, arg1) { \
|
||||
pl[1] = (uint32_t)(arg1); \
|
||||
PM_PACK_PAYLOAD1(pl, (mid), (flag), (arg0)); \
|
||||
}
|
||||
|
||||
#define PM_PACK_PAYLOAD3(pl, mid, flag, arg0, arg1, arg2) { \
|
||||
pl[2] = (uint32_t)(arg2); \
|
||||
PM_PACK_PAYLOAD2(pl, (mid), (flag), (arg0), (arg1)); \
|
||||
}
|
||||
|
||||
#define PM_PACK_PAYLOAD4(pl, mid, flag, arg0, arg1, arg2, arg3) { \
|
||||
pl[3] = (uint32_t)(arg3); \
|
||||
PM_PACK_PAYLOAD3(pl, (mid), (flag), (arg0), (arg1), (arg2)); \
|
||||
}
|
||||
|
||||
#define PM_PACK_PAYLOAD5(pl, mid, flag, arg0, arg1, arg2, arg3, arg4) { \
|
||||
pl[4] = (uint32_t)(arg4); \
|
||||
PM_PACK_PAYLOAD4(pl, (mid), (flag), (arg0), (arg1), (arg2), (arg3)); \
|
||||
}
|
||||
|
||||
#define PM_PACK_PAYLOAD6(pl, mid, flag, arg0, arg1, arg2, arg3, arg4, arg5) { \
|
||||
pl[5] = (uint32_t)(arg5); \
|
||||
PM_PACK_PAYLOAD5(pl, (mid), (flag), (arg0), (arg1), (arg2), (arg3), (arg4)); \
|
||||
}
|
||||
|
||||
#endif /* PM_API_SYS_H */
|
||||
+261
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* APU specific definition of processors in the subsystem as well as functions
|
||||
* for getting information about and changing state of the APU.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <plat_ipi.h>
|
||||
#include <platform_def.h>
|
||||
#include <versal_def.h>
|
||||
#include <lib/bakery_lock.h>
|
||||
#include <lib/mmio.h>
|
||||
#include <lib/utils.h>
|
||||
#include <drivers/arm/gicv3.h>
|
||||
#include <drivers/arm/gic_common.h>
|
||||
#include <plat/common/platform.h>
|
||||
#include "pm_api_sys.h"
|
||||
#include "pm_client.h"
|
||||
#include "pm_defs.h"
|
||||
|
||||
#define UNDEFINED_CPUID (~0)
|
||||
#define IRQ_MAX 142U
|
||||
#define NUM_GICD_ISENABLER ((IRQ_MAX >> 5U) + 1U)
|
||||
|
||||
DEFINE_BAKERY_LOCK(pm_client_secure_lock);
|
||||
|
||||
static const struct pm_ipi apu_ipi = {
|
||||
.local_ipi_id = IPI_ID_APU,
|
||||
.remote_ipi_id = IPI_ID_PMC,
|
||||
.buffer_base = IPI_BUFFER_APU_BASE,
|
||||
};
|
||||
|
||||
/* Order in pm_procs_all array must match cpu ids */
|
||||
static const struct pm_proc pm_procs_all[] = {
|
||||
{
|
||||
.node_id = XPM_DEVID_ACPU_0,
|
||||
.ipi = &apu_ipi,
|
||||
.pwrdn_mask = APU_0_PWRCTL_CPUPWRDWNREQ_MASK,
|
||||
},
|
||||
{
|
||||
.node_id = XPM_DEVID_ACPU_1,
|
||||
.ipi = &apu_ipi,
|
||||
.pwrdn_mask = APU_1_PWRCTL_CPUPWRDWNREQ_MASK,
|
||||
}
|
||||
};
|
||||
|
||||
const struct pm_proc *primary_proc = &pm_procs_all[0];
|
||||
|
||||
/* Interrupt to PM node index map */
|
||||
static enum pm_device_node_idx irq_node_map[IRQ_MAX + 1] = {
|
||||
[13] = XPM_NODEIDX_DEV_GPIO,
|
||||
[14] = XPM_NODEIDX_DEV_I2C_0,
|
||||
[15] = XPM_NODEIDX_DEV_I2C_1,
|
||||
[16] = XPM_NODEIDX_DEV_SPI_0,
|
||||
[17] = XPM_NODEIDX_DEV_SPI_1,
|
||||
[18] = XPM_NODEIDX_DEV_UART_0,
|
||||
[19] = XPM_NODEIDX_DEV_UART_1,
|
||||
[20] = XPM_NODEIDX_DEV_CAN_FD_0,
|
||||
[21] = XPM_NODEIDX_DEV_CAN_FD_1,
|
||||
[22] = XPM_NODEIDX_DEV_USB_0,
|
||||
[23] = XPM_NODEIDX_DEV_USB_0,
|
||||
[24] = XPM_NODEIDX_DEV_USB_0,
|
||||
[25] = XPM_NODEIDX_DEV_USB_0,
|
||||
[26] = XPM_NODEIDX_DEV_USB_0,
|
||||
[37] = XPM_NODEIDX_DEV_TTC_0,
|
||||
[38] = XPM_NODEIDX_DEV_TTC_0,
|
||||
[39] = XPM_NODEIDX_DEV_TTC_0,
|
||||
[40] = XPM_NODEIDX_DEV_TTC_1,
|
||||
[41] = XPM_NODEIDX_DEV_TTC_1,
|
||||
[42] = XPM_NODEIDX_DEV_TTC_1,
|
||||
[43] = XPM_NODEIDX_DEV_TTC_2,
|
||||
[44] = XPM_NODEIDX_DEV_TTC_2,
|
||||
[45] = XPM_NODEIDX_DEV_TTC_2,
|
||||
[46] = XPM_NODEIDX_DEV_TTC_3,
|
||||
[47] = XPM_NODEIDX_DEV_TTC_3,
|
||||
[48] = XPM_NODEIDX_DEV_TTC_3,
|
||||
[56] = XPM_NODEIDX_DEV_GEM_0,
|
||||
[57] = XPM_NODEIDX_DEV_GEM_0,
|
||||
[58] = XPM_NODEIDX_DEV_GEM_1,
|
||||
[59] = XPM_NODEIDX_DEV_GEM_1,
|
||||
[60] = XPM_NODEIDX_DEV_ADMA_0,
|
||||
[61] = XPM_NODEIDX_DEV_ADMA_1,
|
||||
[62] = XPM_NODEIDX_DEV_ADMA_2,
|
||||
[63] = XPM_NODEIDX_DEV_ADMA_3,
|
||||
[64] = XPM_NODEIDX_DEV_ADMA_4,
|
||||
[65] = XPM_NODEIDX_DEV_ADMA_5,
|
||||
[66] = XPM_NODEIDX_DEV_ADMA_6,
|
||||
[67] = XPM_NODEIDX_DEV_ADMA_7,
|
||||
[74] = XPM_NODEIDX_DEV_USB_0,
|
||||
[126] = XPM_NODEIDX_DEV_SDIO_0,
|
||||
[127] = XPM_NODEIDX_DEV_SDIO_0,
|
||||
[128] = XPM_NODEIDX_DEV_SDIO_1,
|
||||
[129] = XPM_NODEIDX_DEV_SDIO_1,
|
||||
[142] = XPM_NODEIDX_DEV_RTC,
|
||||
};
|
||||
|
||||
/**
|
||||
* irq_to_pm_node_idx - Get PM node index corresponding to the interrupt number
|
||||
* @irq: Interrupt number
|
||||
*
|
||||
* Return: PM node index corresponding to the specified interrupt
|
||||
*/
|
||||
static enum pm_device_node_idx irq_to_pm_node_idx(uint32_t irq)
|
||||
{
|
||||
assert(irq <= IRQ_MAX);
|
||||
return irq_node_map[irq];
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_client_set_wakeup_sources - Set all devices with enabled interrupts as
|
||||
* wake sources in the LibPM.
|
||||
* @node_id: Node id of processor
|
||||
*/
|
||||
static void pm_client_set_wakeup_sources(uint32_t node_id)
|
||||
{
|
||||
uint32_t reg_num;
|
||||
uint32_t device_id;
|
||||
uint8_t pm_wakeup_nodes_set[XPM_NODEIDX_DEV_MAX];
|
||||
uintptr_t isenabler1 = PLAT_VERSAL_GICD_BASE + GICD_ISENABLER + 4;
|
||||
|
||||
zeromem(&pm_wakeup_nodes_set, (u_register_t)sizeof(pm_wakeup_nodes_set));
|
||||
|
||||
for (reg_num = 0U; reg_num < NUM_GICD_ISENABLER; reg_num++) {
|
||||
uint32_t base_irq = reg_num << ISENABLER_SHIFT;
|
||||
uint32_t reg = mmio_read_32(isenabler1 + (reg_num << 2));
|
||||
|
||||
if (reg == 0U) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (reg != 0U) {
|
||||
enum pm_device_node_idx node_idx;
|
||||
uint32_t idx, irq, lowest_set = reg & (-reg);
|
||||
enum pm_ret_status ret;
|
||||
|
||||
idx = __builtin_ctz(lowest_set);
|
||||
irq = base_irq + idx;
|
||||
|
||||
if (irq > IRQ_MAX) {
|
||||
break;
|
||||
}
|
||||
|
||||
node_idx = irq_to_pm_node_idx(irq);
|
||||
reg &= ~lowest_set;
|
||||
|
||||
if (node_idx > XPM_NODEIDX_DEV_MIN && node_idx < XPM_NODEIDX_DEV_MAX) {
|
||||
if (pm_wakeup_nodes_set[node_idx] == 0U) {
|
||||
/* Get device ID from node index */
|
||||
device_id = PERIPH_DEVID(node_idx);
|
||||
ret = pm_set_wakeup_source(node_id,
|
||||
device_id, 1,
|
||||
SECURE_FLAG);
|
||||
pm_wakeup_nodes_set[node_idx] = (ret == PM_RET_SUCCESS) ?
|
||||
1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_client_suspend() - Client-specific suspend actions
|
||||
*
|
||||
* This function should contain any PU-specific actions
|
||||
* required prior to sending suspend request to PMU
|
||||
* Actions taken depend on the state system is suspending to.
|
||||
*/
|
||||
void pm_client_suspend(const struct pm_proc *proc, uint32_t state)
|
||||
{
|
||||
bakery_lock_get(&pm_client_secure_lock);
|
||||
|
||||
if (state == PM_STATE_SUSPEND_TO_RAM) {
|
||||
pm_client_set_wakeup_sources((uint32_t)proc->node_id);
|
||||
}
|
||||
|
||||
/* Set powerdown request */
|
||||
mmio_write_32(FPD_APU_PWRCTL, mmio_read_32(FPD_APU_PWRCTL) |
|
||||
(uint32_t)proc->pwrdn_mask);
|
||||
|
||||
bakery_lock_release(&pm_client_secure_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_client_abort_suspend() - Client-specific abort-suspend actions
|
||||
*
|
||||
* This function should contain any PU-specific actions
|
||||
* required for aborting a prior suspend request
|
||||
*/
|
||||
void pm_client_abort_suspend(void)
|
||||
{
|
||||
/* Enable interrupts at processor level (for current cpu) */
|
||||
gicv3_cpuif_enable(plat_my_core_pos());
|
||||
|
||||
bakery_lock_get(&pm_client_secure_lock);
|
||||
|
||||
/* Clear powerdown request */
|
||||
mmio_write_32(FPD_APU_PWRCTL, mmio_read_32(FPD_APU_PWRCTL) &
|
||||
~((uint32_t)primary_proc->pwrdn_mask));
|
||||
|
||||
bakery_lock_release(&pm_client_secure_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_get_cpuid() - get the local cpu ID for a global node ID
|
||||
* @nid: node id of the processor
|
||||
*
|
||||
* Return: the cpu ID (starting from 0) for the subsystem
|
||||
*/
|
||||
static uint32_t pm_get_cpuid(uint32_t nid)
|
||||
{
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(pm_procs_all); i++) {
|
||||
if (pm_procs_all[i].node_id == nid) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return UNDEFINED_CPUID;
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_client_wakeup() - Client-specific wakeup actions
|
||||
*
|
||||
* This function should contain any PU-specific actions
|
||||
* required for waking up another APU core
|
||||
*/
|
||||
void pm_client_wakeup(const struct pm_proc *proc)
|
||||
{
|
||||
uint32_t cpuid = pm_get_cpuid(proc->node_id);
|
||||
|
||||
if (cpuid == UNDEFINED_CPUID) {
|
||||
return;
|
||||
}
|
||||
|
||||
bakery_lock_get(&pm_client_secure_lock);
|
||||
|
||||
/* clear powerdown bit for affected cpu */
|
||||
uint32_t val = mmio_read_32(FPD_APU_PWRCTL);
|
||||
val &= ~(proc->pwrdn_mask);
|
||||
mmio_write_32(FPD_APU_PWRCTL, val);
|
||||
|
||||
bakery_lock_release(&pm_client_secure_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_get_proc() - returns pointer to the proc structure
|
||||
* @cpuid: id of the cpu whose proc struct pointer should be returned
|
||||
*
|
||||
* Return: pointer to a proc structure if proc is found, otherwise NULL
|
||||
*/
|
||||
const struct pm_proc *pm_get_proc(uint32_t cpuid)
|
||||
{
|
||||
if (cpuid < ARRAY_SIZE(pm_procs_all)) {
|
||||
return &pm_procs_all[cpuid];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/* Versal power management enums and defines */
|
||||
|
||||
#ifndef PM_DEFS_H
|
||||
#define PM_DEFS_H
|
||||
|
||||
#include "pm_node.h"
|
||||
|
||||
/*********************************************************************
|
||||
* Macro definitions
|
||||
********************************************************************/
|
||||
|
||||
/* State arguments of the self suspend */
|
||||
#define PM_STATE_CPU_IDLE 0x0U
|
||||
#define PM_STATE_SUSPEND_TO_RAM 0xFU
|
||||
|
||||
#define MAX_LATENCY (~0U)
|
||||
#define MAX_QOS 100U
|
||||
|
||||
/* Processor core device IDs */
|
||||
#define APU_DEVID(IDX) NODEID(XPM_NODECLASS_DEVICE, XPM_NODESUBCL_DEV_CORE, \
|
||||
XPM_NODETYPE_DEV_CORE_APU, (IDX))
|
||||
|
||||
#define XPM_DEVID_ACPU_0 APU_DEVID(XPM_NODEIDX_DEV_ACPU_0)
|
||||
#define XPM_DEVID_ACPU_1 APU_DEVID(XPM_NODEIDX_DEV_ACPU_1)
|
||||
|
||||
#define PERIPH_DEVID(IDX) NODEID(XPM_NODECLASS_DEVICE, \
|
||||
XPM_NODESUBCL_DEV_PERIPH, \
|
||||
XPM_NODETYPE_DEV_PERIPH, (IDX))
|
||||
|
||||
#define PM_GET_CALLBACK_DATA 0xa01U
|
||||
#define PM_GET_TRUSTZONE_VERSION 0xa03U
|
||||
#define TF_A_PM_REGISTER_SGI 0xa04U
|
||||
|
||||
/* PM API Versions */
|
||||
#define PM_API_BASE_VERSION 1U
|
||||
#define PM_API_VERSION_2 2U
|
||||
|
||||
/* PM API ids */
|
||||
#define PM_REGISTER_NOTIFIER 5U
|
||||
#define PM_REQ_SUSPEND 6U
|
||||
#define PM_SELF_SUSPEND 7U
|
||||
#define PM_FORCE_POWERDOWN 8U
|
||||
#define PM_ABORT_SUSPEND 9U
|
||||
#define PM_REQ_WAKEUP 10U
|
||||
#define PM_SET_WAKEUP_SOURCE 11U
|
||||
#define PM_SYSTEM_SHUTDOWN 12U
|
||||
#define PM_IOCTL 34U
|
||||
#define PM_QUERY_DATA 35U
|
||||
#define PM_PLL_SET_PARAMETER 48U
|
||||
#define PM_PLL_GET_PARAMETER 49U
|
||||
#define PM_PLL_SET_MODE 50U
|
||||
#define PM_PLL_GET_MODE 51U
|
||||
#define PM_FEATURE_CHECK 63U
|
||||
|
||||
/* Loader API ids */
|
||||
#define PM_LOAD_PDI 0x701U
|
||||
#define PM_LOAD_GET_HANDOFF_PARAMS 0x70BU
|
||||
|
||||
/* IOCTL IDs for clock driver */
|
||||
#define IOCTL_SET_PLL_FRAC_MODE 8U
|
||||
#define IOCTL_GET_PLL_FRAC_MODE 9U
|
||||
#define IOCTL_SET_PLL_FRAC_DATA 10U
|
||||
#define IOCTL_GET_PLL_FRAC_DATA 11U
|
||||
#define IOCTL_SET_SGI 25U
|
||||
|
||||
/* Parameter ID for PLL IOCTLs */
|
||||
/* Fractional data portion for PLL */
|
||||
#define PM_PLL_PARAM_DATA 2
|
||||
|
||||
/* System shutdown macros */
|
||||
#define XPM_SHUTDOWN_TYPE_SHUTDOWN 0U
|
||||
#define XPM_SHUTDOWN_TYPE_RESET 1U
|
||||
#define XPM_SHUTDOWN_TYPE_SETSCOPE_ONLY 2U
|
||||
|
||||
#define XPM_SHUTDOWN_SUBTYPE_RST_SUBSYSTEM 0U
|
||||
#define XPM_SHUTDOWN_SUBTYPE_RST_PS_ONLY 1U
|
||||
#define XPM_SHUTDOWN_SUBTYPE_RST_SYSTEM 2U
|
||||
|
||||
/*********************************************************************
|
||||
* Enum definitions
|
||||
********************************************************************/
|
||||
|
||||
enum pm_abort_reason {
|
||||
ABORT_REASON_WKUP_EVENT = 100,
|
||||
ABORT_REASON_PU_BUSY,
|
||||
ABORT_REASON_NO_PWRDN,
|
||||
ABORT_REASON_UNKNOWN,
|
||||
};
|
||||
|
||||
enum pm_opchar_type {
|
||||
PM_OPCHAR_TYPE_POWER = 1,
|
||||
PM_OPCHAR_TYPE_TEMP,
|
||||
PM_OPCHAR_TYPE_LATENCY,
|
||||
};
|
||||
|
||||
/**
|
||||
* Subsystem IDs
|
||||
*/
|
||||
typedef enum {
|
||||
XPM_SUBSYSID_PMC,
|
||||
XPM_SUBSYSID_PSM,
|
||||
XPM_SUBSYSID_APU,
|
||||
XPM_SUBSYSID_RPU0_LOCK,
|
||||
XPM_SUBSYSID_RPU0_0,
|
||||
XPM_SUBSYSID_RPU0_1,
|
||||
XPM_SUBSYSID_DDR0,
|
||||
XPM_SUBSYSID_ME,
|
||||
XPM_SUBSYSID_PL,
|
||||
XPM_SUBSYSID_MAX,
|
||||
} XPm_SubsystemId;
|
||||
|
||||
/**
|
||||
* @PM_RET_SUCCESS: success
|
||||
* @PM_RET_ERROR_ARGS: illegal arguments provided (deprecated)
|
||||
* @PM_RET_ERROR_NOTSUPPORTED: feature not supported (deprecated)
|
||||
* @PM_RET_ERROR_NOFEATURE: feature is not available
|
||||
* @PM_RET_ERROR_INTERNAL: internal error
|
||||
* @PM_RET_ERROR_CONFLICT: conflict
|
||||
* @PM_RET_ERROR_ACCESS: access rights violation
|
||||
* @PM_RET_ERROR_INVALID_NODE: invalid node
|
||||
* @PM_RET_ERROR_DOUBLE_REQ: duplicate request for same node
|
||||
* @PM_RET_ERROR_ABORT_SUSPEND: suspend procedure has been aborted
|
||||
* @PM_RET_ERROR_TIMEOUT: timeout in communication with PMU
|
||||
* @PM_RET_ERROR_NODE_USED: node is already in use
|
||||
*/
|
||||
enum pm_ret_status {
|
||||
PM_RET_SUCCESS,
|
||||
PM_RET_ERROR_ARGS = 1,
|
||||
PM_RET_ERROR_NOTSUPPORTED = 4,
|
||||
PM_RET_ERROR_NOFEATURE = 19,
|
||||
PM_RET_ERROR_INTERNAL = 2000,
|
||||
PM_RET_ERROR_CONFLICT = 2001,
|
||||
PM_RET_ERROR_ACCESS = 2002,
|
||||
PM_RET_ERROR_INVALID_NODE = 2003,
|
||||
PM_RET_ERROR_DOUBLE_REQ = 2004,
|
||||
PM_RET_ERROR_ABORT_SUSPEND = 2005,
|
||||
PM_RET_ERROR_TIMEOUT = 2006,
|
||||
PM_RET_ERROR_NODE_USED = 2007
|
||||
};
|
||||
|
||||
/**
|
||||
* Qids
|
||||
*/
|
||||
enum pm_query_id {
|
||||
XPM_QID_INVALID,
|
||||
XPM_QID_CLOCK_GET_NAME,
|
||||
XPM_QID_CLOCK_GET_TOPOLOGY,
|
||||
XPM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS,
|
||||
XPM_QID_CLOCK_GET_MUXSOURCES,
|
||||
XPM_QID_CLOCK_GET_ATTRIBUTES,
|
||||
XPM_QID_PINCTRL_GET_NUM_PINS,
|
||||
XPM_QID_PINCTRL_GET_NUM_FUNCTIONS,
|
||||
XPM_QID_PINCTRL_GET_NUM_FUNCTION_GROUPS,
|
||||
XPM_QID_PINCTRL_GET_FUNCTION_NAME,
|
||||
XPM_QID_PINCTRL_GET_FUNCTION_GROUPS,
|
||||
XPM_QID_PINCTRL_GET_PIN_GROUPS,
|
||||
XPM_QID_CLOCK_GET_NUM_CLOCKS,
|
||||
XPM_QID_CLOCK_GET_MAX_DIVISOR,
|
||||
XPM_QID_PLD_GET_PARENT,
|
||||
};
|
||||
#endif /* PM_DEFS_H */
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Xilinx, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/* Versal PM nodes enums and defines */
|
||||
|
||||
#ifndef PM_NODE_H
|
||||
#define PM_NODE_H
|
||||
|
||||
/*********************************************************************
|
||||
* Macro definitions
|
||||
********************************************************************/
|
||||
|
||||
#define NODE_CLASS_SHIFT 26U
|
||||
#define NODE_SUBCLASS_SHIFT 20U
|
||||
#define NODE_TYPE_SHIFT 14U
|
||||
#define NODE_INDEX_SHIFT 0U
|
||||
#define NODE_CLASS_MASK_BITS 0x3F
|
||||
#define NODE_SUBCLASS_MASK_BITS 0x3F
|
||||
#define NODE_TYPE_MASK_BITS 0x3F
|
||||
#define NODE_INDEX_MASK_BITS 0x3FFF
|
||||
#define NODE_CLASS_MASK (NODE_CLASS_MASK_BITS << NODE_CLASS_SHIFT)
|
||||
#define NODE_SUBCLASS_MASK (NODE_SUBCLASS_MASK_BITS << NODE_SUBCLASS_SHIFT)
|
||||
#define NODE_TYPE_MASK (NODE_TYPE_MASK_BITS << NODE_TYPE_SHIFT)
|
||||
#define NODE_INDEX_MASK (NODE_INDEX_MASK_BITS << NODE_INDEX_SHIFT)
|
||||
|
||||
#define NODEID(CLASS, SUBCLASS, TYPE, INDEX) \
|
||||
((((CLASS) & NODE_CLASS_MASK_BITS) << NODE_CLASS_SHIFT) | \
|
||||
(((SUBCLASS) & NODE_SUBCLASS_MASK_BITS) << NODE_SUBCLASS_SHIFT) | \
|
||||
(((TYPE) & NODE_TYPE_MASK_BITS) << NODE_TYPE_SHIFT) | \
|
||||
(((INDEX) & NODE_INDEX_MASK_BITS) << NODE_INDEX_SHIFT))
|
||||
|
||||
#define NODECLASS(ID) (((ID) & NODE_CLASS_MASK) >> NODE_CLASS_SHIFT)
|
||||
#define NODESUBCLASS(ID) (((ID) & NODE_SUBCLASS_MASK) >> \
|
||||
NODE_SUBCLASS_SHIFT)
|
||||
#define NODETYPE(ID) (((ID) & NODE_TYPE_MASK) >> NODE_TYPE_SHIFT)
|
||||
#define NODEINDEX(ID) (((ID) & NODE_INDEX_MASK) >> NODE_INDEX_SHIFT)
|
||||
|
||||
/*********************************************************************
|
||||
* Enum definitions
|
||||
********************************************************************/
|
||||
|
||||
/* Node class types */
|
||||
enum pm_node_class {
|
||||
XPM_NODECLASS_MIN,
|
||||
|
||||
XPM_NODECLASS_POWER,
|
||||
XPM_NODECLASS_CLOCK,
|
||||
XPM_NODECLASS_RESET,
|
||||
XPM_NODECLASS_MEMIC,
|
||||
XPM_NODECLASS_STMIC,
|
||||
XPM_NODECLASS_DEVICE,
|
||||
|
||||
XPM_NODECLASS_MAX
|
||||
};
|
||||
|
||||
enum pm_device_node_subclass {
|
||||
/* Device types */
|
||||
XPM_NODESUBCL_DEV_CORE = 1,
|
||||
XPM_NODESUBCL_DEV_PERIPH,
|
||||
XPM_NODESUBCL_DEV_MEM,
|
||||
XPM_NODESUBCL_DEV_SOC,
|
||||
XPM_NODESUBCL_DEV_MEM_CTRLR,
|
||||
XPM_NODESUBCL_DEV_PHY,
|
||||
};
|
||||
|
||||
enum pm_device_node_type {
|
||||
/* Device types */
|
||||
XPM_NODETYPE_DEV_CORE_PMC = 1,
|
||||
XPM_NODETYPE_DEV_CORE_PSM,
|
||||
XPM_NODETYPE_DEV_CORE_APU,
|
||||
XPM_NODETYPE_DEV_CORE_RPU,
|
||||
XPM_NODETYPE_DEV_OCM,
|
||||
XPM_NODETYPE_DEV_TCM,
|
||||
XPM_NODETYPE_DEV_L2CACHE,
|
||||
XPM_NODETYPE_DEV_DDR,
|
||||
XPM_NODETYPE_DEV_PERIPH,
|
||||
XPM_NODETYPE_DEV_SOC,
|
||||
XPM_NODETYPE_DEV_GT,
|
||||
};
|
||||
|
||||
/* Device node Indexes */
|
||||
enum pm_device_node_idx {
|
||||
/* Device nodes */
|
||||
XPM_NODEIDX_DEV_MIN,
|
||||
|
||||
/* Processor devices */
|
||||
XPM_NODEIDX_DEV_PMC_PROC,
|
||||
XPM_NODEIDX_DEV_PSM_PROC,
|
||||
XPM_NODEIDX_DEV_ACPU_0,
|
||||
XPM_NODEIDX_DEV_ACPU_1,
|
||||
XPM_NODEIDX_DEV_RPU0_0,
|
||||
XPM_NODEIDX_DEV_RPU0_1,
|
||||
|
||||
/* Memory devices */
|
||||
XPM_NODEIDX_DEV_OCM_0,
|
||||
XPM_NODEIDX_DEV_OCM_1,
|
||||
XPM_NODEIDX_DEV_OCM_2,
|
||||
XPM_NODEIDX_DEV_OCM_3,
|
||||
XPM_NODEIDX_DEV_TCM_0_A,
|
||||
XPM_NODEIDX_DEV_TCM_0_B,
|
||||
XPM_NODEIDX_DEV_TCM_1_A,
|
||||
XPM_NODEIDX_DEV_TCM_1_B,
|
||||
XPM_NODEIDX_DEV_L2_BANK_0,
|
||||
XPM_NODEIDX_DEV_DDR_0,
|
||||
XPM_NODEIDX_DEV_DDR_1,
|
||||
XPM_NODEIDX_DEV_DDR_2,
|
||||
XPM_NODEIDX_DEV_DDR_3,
|
||||
XPM_NODEIDX_DEV_DDR_4,
|
||||
XPM_NODEIDX_DEV_DDR_5,
|
||||
XPM_NODEIDX_DEV_DDR_6,
|
||||
XPM_NODEIDX_DEV_DDR_7,
|
||||
|
||||
/* LPD Peripheral devices */
|
||||
XPM_NODEIDX_DEV_USB_0,
|
||||
XPM_NODEIDX_DEV_GEM_0,
|
||||
XPM_NODEIDX_DEV_GEM_1,
|
||||
XPM_NODEIDX_DEV_SPI_0,
|
||||
XPM_NODEIDX_DEV_SPI_1,
|
||||
XPM_NODEIDX_DEV_I2C_0,
|
||||
XPM_NODEIDX_DEV_I2C_1,
|
||||
XPM_NODEIDX_DEV_CAN_FD_0,
|
||||
XPM_NODEIDX_DEV_CAN_FD_1,
|
||||
XPM_NODEIDX_DEV_UART_0,
|
||||
XPM_NODEIDX_DEV_UART_1,
|
||||
XPM_NODEIDX_DEV_GPIO,
|
||||
XPM_NODEIDX_DEV_TTC_0,
|
||||
XPM_NODEIDX_DEV_TTC_1,
|
||||
XPM_NODEIDX_DEV_TTC_2,
|
||||
XPM_NODEIDX_DEV_TTC_3,
|
||||
XPM_NODEIDX_DEV_SWDT_LPD,
|
||||
|
||||
/* FPD Peripheral devices */
|
||||
XPM_NODEIDX_DEV_SWDT_FPD,
|
||||
|
||||
/* PMC Peripheral devices */
|
||||
XPM_NODEIDX_DEV_OSPI,
|
||||
XPM_NODEIDX_DEV_QSPI,
|
||||
XPM_NODEIDX_DEV_GPIO_PMC,
|
||||
XPM_NODEIDX_DEV_I2C_PMC,
|
||||
XPM_NODEIDX_DEV_SDIO_0,
|
||||
XPM_NODEIDX_DEV_SDIO_1,
|
||||
|
||||
XPM_NODEIDX_DEV_PL_0,
|
||||
XPM_NODEIDX_DEV_PL_1,
|
||||
XPM_NODEIDX_DEV_PL_2,
|
||||
XPM_NODEIDX_DEV_PL_3,
|
||||
XPM_NODEIDX_DEV_RTC,
|
||||
XPM_NODEIDX_DEV_ADMA_0,
|
||||
XPM_NODEIDX_DEV_ADMA_1,
|
||||
XPM_NODEIDX_DEV_ADMA_2,
|
||||
XPM_NODEIDX_DEV_ADMA_3,
|
||||
XPM_NODEIDX_DEV_ADMA_4,
|
||||
XPM_NODEIDX_DEV_ADMA_5,
|
||||
XPM_NODEIDX_DEV_ADMA_6,
|
||||
XPM_NODEIDX_DEV_ADMA_7,
|
||||
XPM_NODEIDX_DEV_IPI_0,
|
||||
XPM_NODEIDX_DEV_IPI_1,
|
||||
XPM_NODEIDX_DEV_IPI_2,
|
||||
XPM_NODEIDX_DEV_IPI_3,
|
||||
XPM_NODEIDX_DEV_IPI_4,
|
||||
XPM_NODEIDX_DEV_IPI_5,
|
||||
XPM_NODEIDX_DEV_IPI_6,
|
||||
|
||||
/* Entire SoC */
|
||||
XPM_NODEIDX_DEV_SOC,
|
||||
|
||||
/* DDR memory controllers */
|
||||
XPM_NODEIDX_DEV_DDRMC_0,
|
||||
XPM_NODEIDX_DEV_DDRMC_1,
|
||||
XPM_NODEIDX_DEV_DDRMC_2,
|
||||
XPM_NODEIDX_DEV_DDRMC_3,
|
||||
|
||||
/* GT devices */
|
||||
XPM_NODEIDX_DEV_GT_0,
|
||||
XPM_NODEIDX_DEV_GT_1,
|
||||
XPM_NODEIDX_DEV_GT_2,
|
||||
XPM_NODEIDX_DEV_GT_3,
|
||||
XPM_NODEIDX_DEV_GT_4,
|
||||
XPM_NODEIDX_DEV_GT_5,
|
||||
XPM_NODEIDX_DEV_GT_6,
|
||||
XPM_NODEIDX_DEV_GT_7,
|
||||
XPM_NODEIDX_DEV_GT_8,
|
||||
XPM_NODEIDX_DEV_GT_9,
|
||||
XPM_NODEIDX_DEV_GT_10,
|
||||
|
||||
XPM_NODEIDX_DEV_MAX
|
||||
};
|
||||
|
||||
#endif /* PM_NODE_H */
|
||||
+395
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved.
|
||||
* Copyright (c) 2022, Advanced Micro Devices, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* Top-level SMC handler for Versal power management calls and
|
||||
* IPI setup functions for communication with PMC.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <plat_private.h>
|
||||
#include <stdbool.h>
|
||||
#include <common/runtime_svc.h>
|
||||
#include <plat/common/platform.h>
|
||||
#include "pm_api_sys.h"
|
||||
#include "pm_client.h"
|
||||
#include "pm_ipi.h"
|
||||
#include <drivers/arm/gicv3.h>
|
||||
#include "../drivers/arm/gic/v3/gicv3_private.h"
|
||||
|
||||
#define MODE 0x80000000U
|
||||
|
||||
#define XSCUGIC_SGIR_EL1_INITID_SHIFT 24U
|
||||
#define INVALID_SGI 0xFFU
|
||||
#define PM_INIT_SUSPEND_CB (30U)
|
||||
#define PM_NOTIFY_CB (32U)
|
||||
DEFINE_RENAME_SYSREG_RW_FUNCS(icc_asgi1r_el1, S3_0_C12_C11_6)
|
||||
|
||||
/* pm_up = true - UP, pm_up = false - DOWN */
|
||||
static bool pm_up;
|
||||
static uint32_t sgi = (uint32_t)INVALID_SGI;
|
||||
|
||||
static void notify_os(void)
|
||||
{
|
||||
int32_t cpu;
|
||||
uint32_t reg;
|
||||
|
||||
cpu = plat_my_core_pos() + 1U;
|
||||
|
||||
reg = (cpu | (sgi << XSCUGIC_SGIR_EL1_INITID_SHIFT));
|
||||
write_icc_asgi1r_el1(reg);
|
||||
}
|
||||
|
||||
static uint64_t ipi_fiq_handler(uint32_t id, uint32_t flags, void *handle,
|
||||
void *cookie)
|
||||
{
|
||||
uint32_t payload[4] = {0};
|
||||
|
||||
VERBOSE("Received IPI FIQ from firmware\n");
|
||||
|
||||
(void)plat_ic_acknowledge_interrupt();
|
||||
|
||||
pm_get_callbackdata(payload, ARRAY_SIZE(payload), 0, 0);
|
||||
switch (payload[0]) {
|
||||
case PM_INIT_SUSPEND_CB:
|
||||
case PM_NOTIFY_CB:
|
||||
if (sgi != INVALID_SGI) {
|
||||
notify_os();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pm_ipi_irq_clear(primary_proc);
|
||||
WARN("Invalid IPI payload\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Clear FIQ */
|
||||
plat_ic_end_of_interrupt(id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_register_sgi() - PM register the IPI interrupt
|
||||
*
|
||||
* @sgi - SGI number to be used for communication.
|
||||
* @reset - Reset to invalid SGI when reset=1.
|
||||
* @return On success, the initialization function must return 0.
|
||||
* Any other return value will cause the framework to ignore
|
||||
* the service
|
||||
*
|
||||
* Update the SGI number to be used.
|
||||
*
|
||||
*/
|
||||
int32_t pm_register_sgi(uint32_t sgi_num, uint32_t reset)
|
||||
{
|
||||
if (reset == 1U) {
|
||||
sgi = INVALID_SGI;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sgi != INVALID_SGI) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (sgi_num >= GICV3_MAX_SGI_TARGETS) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sgi = (uint32_t)sgi_num;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_setup() - PM service setup
|
||||
*
|
||||
* @return On success, the initialization function must return 0.
|
||||
* Any other return value will cause the framework to ignore
|
||||
* the service
|
||||
*
|
||||
* Initialization functions for Versal power management for
|
||||
* communicaton with PMC.
|
||||
*
|
||||
* Called from sip_svc_setup initialization function with the
|
||||
* rt_svc_init signature.
|
||||
*/
|
||||
int32_t pm_setup(void)
|
||||
{
|
||||
int32_t ret = 0;
|
||||
|
||||
pm_ipi_init(primary_proc);
|
||||
pm_up = true;
|
||||
|
||||
/*
|
||||
* Enable IPI IRQ
|
||||
* assume the rich OS is OK to handle callback IRQs now.
|
||||
* Even if we were wrong, it would not enable the IRQ in
|
||||
* the GIC.
|
||||
*/
|
||||
pm_ipi_irq_enable(primary_proc);
|
||||
|
||||
ret = request_intr_type_el3(PLAT_VERSAL_IPI_IRQ, ipi_fiq_handler);
|
||||
if (ret != 0) {
|
||||
WARN("BL31: registering IPI interrupt failed\n");
|
||||
}
|
||||
|
||||
gicd_write_irouter(gicv3_driver_data->gicd_base, PLAT_VERSAL_IPI_IRQ, MODE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* eemi_for_compatibility() - EEMI calls handler for deprecated calls
|
||||
*
|
||||
* @return - If EEMI API found then, uintptr_t type address, else 0
|
||||
*
|
||||
* Some EEMI API's use case needs to be changed in Linux driver, so they
|
||||
* can take advantage of common EEMI handler in TF-A. As of now the old
|
||||
* implementation of these APIs are required to maintain backward compatibility
|
||||
* until their use case in linux driver changes.
|
||||
*/
|
||||
static uintptr_t eemi_for_compatibility(uint32_t api_id, uint32_t *pm_arg,
|
||||
void *handle, uint32_t security_flag)
|
||||
{
|
||||
enum pm_ret_status ret;
|
||||
|
||||
switch (api_id) {
|
||||
|
||||
case PM_IOCTL:
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
ret = pm_api_ioctl(pm_arg[0], pm_arg[1], pm_arg[2],
|
||||
pm_arg[3], pm_arg[4],
|
||||
&value, security_flag);
|
||||
if (ret == PM_RET_ERROR_NOTSUPPORTED)
|
||||
return (uintptr_t)0;
|
||||
|
||||
SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32U);
|
||||
}
|
||||
|
||||
case PM_QUERY_DATA:
|
||||
{
|
||||
uint32_t data[PAYLOAD_ARG_CNT] = { 0 };
|
||||
|
||||
ret = pm_query_data(pm_arg[0], pm_arg[1], pm_arg[2],
|
||||
pm_arg[3], data, security_flag);
|
||||
|
||||
SMC_RET2(handle, (uint64_t)ret | ((uint64_t)data[0] << 32U),
|
||||
(uint64_t)data[1] | ((uint64_t)data[2] << 32U));
|
||||
}
|
||||
|
||||
case PM_FEATURE_CHECK:
|
||||
{
|
||||
uint32_t result[PAYLOAD_ARG_CNT] = {0U};
|
||||
|
||||
ret = pm_feature_check(pm_arg[0], result, security_flag);
|
||||
SMC_RET2(handle, (uint64_t)ret | ((uint64_t)result[0] << 32U),
|
||||
(uint64_t)result[1] | ((uint64_t)result[2] << 32U));
|
||||
}
|
||||
|
||||
case PM_LOAD_PDI:
|
||||
{
|
||||
ret = pm_load_pdi(pm_arg[0], pm_arg[1], pm_arg[2],
|
||||
security_flag);
|
||||
SMC_RET1(handle, (uint64_t)ret);
|
||||
}
|
||||
|
||||
default:
|
||||
return (uintptr_t)0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* eemi_psci_debugfs_handler() - EEMI API invoked from PSCI
|
||||
*
|
||||
* These EEMI APIs performs CPU specific power management tasks.
|
||||
* These EEMI APIs are invoked either from PSCI or from debugfs in kernel.
|
||||
* These calls require CPU specific processing before sending IPI request to
|
||||
* Platform Management Controller. For example enable/disable CPU specific
|
||||
* interrupts. This requires separate handler for these calls and may not be
|
||||
* handled using common eemi handler
|
||||
*/
|
||||
static uintptr_t eemi_psci_debugfs_handler(uint32_t api_id, uint32_t *pm_arg,
|
||||
void *handle, uint32_t security_flag)
|
||||
{
|
||||
enum pm_ret_status ret;
|
||||
|
||||
switch (api_id) {
|
||||
|
||||
case PM_SELF_SUSPEND:
|
||||
ret = pm_self_suspend(pm_arg[0], pm_arg[1], pm_arg[2],
|
||||
pm_arg[3], security_flag);
|
||||
SMC_RET1(handle, (u_register_t)ret);
|
||||
|
||||
case PM_FORCE_POWERDOWN:
|
||||
ret = pm_force_powerdown(pm_arg[0], pm_arg[1], security_flag);
|
||||
SMC_RET1(handle, (u_register_t)ret);
|
||||
|
||||
case PM_REQ_SUSPEND:
|
||||
ret = pm_req_suspend(pm_arg[0], pm_arg[1], pm_arg[2],
|
||||
pm_arg[3], security_flag);
|
||||
SMC_RET1(handle, (u_register_t)ret);
|
||||
|
||||
case PM_ABORT_SUSPEND:
|
||||
ret = pm_abort_suspend(pm_arg[0], security_flag);
|
||||
SMC_RET1(handle, (u_register_t)ret);
|
||||
|
||||
case PM_SYSTEM_SHUTDOWN:
|
||||
ret = pm_system_shutdown(pm_arg[0], pm_arg[1], security_flag);
|
||||
SMC_RET1(handle, (u_register_t)ret);
|
||||
|
||||
default:
|
||||
return (uintptr_t)0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TF_A_specific_handler() - SMC handler for TF-A specific functionality
|
||||
*
|
||||
* These EEMI calls performs functionality that does not require
|
||||
* IPI transaction. The handler ends in TF-A and returns requested data to
|
||||
* kernel from TF-A.
|
||||
*/
|
||||
static uintptr_t TF_A_specific_handler(uint32_t api_id, uint32_t *pm_arg,
|
||||
void *handle, uint32_t security_flag)
|
||||
{
|
||||
switch (api_id) {
|
||||
|
||||
case TF_A_PM_REGISTER_SGI:
|
||||
{
|
||||
int32_t ret;
|
||||
|
||||
ret = pm_register_sgi(pm_arg[0], pm_arg[1]);
|
||||
if (ret != 0) {
|
||||
SMC_RET1(handle, (uint32_t)PM_RET_ERROR_ARGS);
|
||||
}
|
||||
|
||||
SMC_RET1(handle, (uint32_t)PM_RET_SUCCESS);
|
||||
}
|
||||
|
||||
case PM_GET_CALLBACK_DATA:
|
||||
{
|
||||
uint32_t result[4] = {0};
|
||||
|
||||
pm_get_callbackdata(result, ARRAY_SIZE(result), security_flag, 1U);
|
||||
SMC_RET2(handle,
|
||||
(uint64_t)result[0] | ((uint64_t)result[1] << 32U),
|
||||
(uint64_t)result[2] | ((uint64_t)result[3] << 32U));
|
||||
}
|
||||
|
||||
case PM_GET_TRUSTZONE_VERSION:
|
||||
SMC_RET1(handle, (uint64_t)PM_RET_SUCCESS |
|
||||
((uint64_t)TZ_VERSION << 32U));
|
||||
|
||||
default:
|
||||
return (uintptr_t)0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* eemi_handler() - Prepare EEMI payload and perform IPI transaction
|
||||
*
|
||||
* EEMI - Embedded Energy Management Interface is Xilinx proprietary protocol
|
||||
* to allow communication between power management controller and different
|
||||
* processing clusters.
|
||||
*
|
||||
* This handler prepares EEMI protocol payload received from kernel and performs
|
||||
* IPI transaction.
|
||||
*/
|
||||
static uintptr_t eemi_handler(uint32_t api_id, uint32_t *pm_arg,
|
||||
void *handle, uint32_t security_flag)
|
||||
{
|
||||
enum pm_ret_status ret;
|
||||
uint32_t buf[PAYLOAD_ARG_CNT] = {0};
|
||||
|
||||
ret = pm_handle_eemi_call(security_flag, api_id, pm_arg[0], pm_arg[1],
|
||||
pm_arg[2], pm_arg[3], pm_arg[4],
|
||||
(uint64_t *)buf);
|
||||
/*
|
||||
* Two IOCTLs, to get clock name and pinctrl name of pm_query_data API
|
||||
* receives 5 words of respoonse from firmware. Currently linux driver can
|
||||
* receive only 4 words from TF-A. So, this needs to be handled separately
|
||||
* than other eemi calls.
|
||||
*/
|
||||
if (api_id == PM_QUERY_DATA) {
|
||||
if ((pm_arg[0] == XPM_QID_CLOCK_GET_NAME ||
|
||||
pm_arg[0] == XPM_QID_PINCTRL_GET_FUNCTION_NAME) &&
|
||||
ret == PM_RET_SUCCESS) {
|
||||
SMC_RET2(handle, (uint64_t)buf[0] | ((uint64_t)buf[1] << 32U),
|
||||
(uint64_t)buf[2] | ((uint64_t)buf[3] << 32U));
|
||||
}
|
||||
}
|
||||
|
||||
SMC_RET2(handle, (uint64_t)ret | ((uint64_t)buf[0] << 32U),
|
||||
(uint64_t)buf[1] | ((uint64_t)buf[2] << 32U));
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_smc_handler() - SMC handler for PM-API calls coming from EL1/EL2.
|
||||
* @smc_fid - Function Identifier
|
||||
* @x1 - x4 - SMC64 Arguments from kernel
|
||||
* x3 (upper 32-bits) and x4 are Unused
|
||||
* @cookie - Unused
|
||||
* @handler - Pointer to caller's context structure
|
||||
*
|
||||
* @return - Unused
|
||||
*
|
||||
* Determines that smc_fid is valid and supported PM SMC Function ID from the
|
||||
* list of pm_api_ids, otherwise completes the request with
|
||||
* the unknown SMC Function ID
|
||||
*
|
||||
* The SMC calls for PM service are forwarded from SIP Service SMC handler
|
||||
* function with rt_svc_handle signature
|
||||
*/
|
||||
uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3,
|
||||
uint64_t x4, const void *cookie, void *handle, uint64_t flags)
|
||||
{
|
||||
uintptr_t ret;
|
||||
uint32_t pm_arg[PAYLOAD_ARG_CNT] = {0};
|
||||
uint32_t security_flag = SECURE_FLAG;
|
||||
uint32_t api_id;
|
||||
|
||||
/* Handle case where PM wasn't initialized properly */
|
||||
if (pm_up == false) {
|
||||
SMC_RET1(handle, SMC_UNK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark BIT24 payload (i.e 1st bit of pm_arg[3] ) as non-secure (1)
|
||||
* if smc called is non secure
|
||||
*/
|
||||
if (is_caller_non_secure(flags) != 0) {
|
||||
security_flag = NON_SECURE_FLAG;
|
||||
}
|
||||
|
||||
pm_arg[0] = (uint32_t)x1;
|
||||
pm_arg[1] = (uint32_t)(x1 >> 32U);
|
||||
pm_arg[2] = (uint32_t)x2;
|
||||
pm_arg[3] = (uint32_t)(x2 >> 32U);
|
||||
pm_arg[4] = (uint32_t)x3;
|
||||
(void)(x4);
|
||||
api_id = smc_fid & FUNCID_NUM_MASK;
|
||||
|
||||
ret = eemi_for_compatibility(api_id, pm_arg, handle, security_flag);
|
||||
if (ret != (uintptr_t)0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = eemi_psci_debugfs_handler(api_id, pm_arg, handle, flags);
|
||||
if (ret != (uintptr_t)0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = TF_A_specific_handler(api_id, pm_arg, handle, security_flag);
|
||||
if (ret != (uintptr_t)0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = eemi_handler(api_id, pm_arg, handle, security_flag);
|
||||
|
||||
return ret;
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef PM_SVC_MAIN_H
|
||||
#define PM_SVC_MAIN_H
|
||||
|
||||
#include <pm_common.h>
|
||||
|
||||
int32_t pm_setup(void);
|
||||
uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3,
|
||||
uint64_t x4, const void *cookie, void *handle,
|
||||
uint64_t flags);
|
||||
|
||||
int32_t pm_register_sgi(uint32_t sgi_num, uint32_t reset);
|
||||
#endif /* PM_SVC_MAIN_H */
|
||||
Reference in New Issue
Block a user