|
|
@ -10,19 +10,30 @@ |
|
|
|
*/ |
|
|
|
|
|
|
|
#include <errno.h> |
|
|
|
#include <gic_common.h> |
|
|
|
#include <runtime_svc.h> |
|
|
|
#include <string.h> |
|
|
|
#include "../zynqmp_private.h" |
|
|
|
#include "pm_api_sys.h" |
|
|
|
#include "pm_client.h" |
|
|
|
#include "pm_ipi.h" |
|
|
|
|
|
|
|
#define PM_GET_CALLBACK_DATA 0xa01 |
|
|
|
#if ZYNQMP_WDT_RESTART |
|
|
|
#include <arch_helpers.h> |
|
|
|
#include <gicv2.h> |
|
|
|
#include <mmio.h> |
|
|
|
#include <platform.h> |
|
|
|
#include <spinlock.h> |
|
|
|
#endif |
|
|
|
|
|
|
|
#define PM_SET_SUSPEND_MODE 0xa02 |
|
|
|
#define PM_GET_TRUSTZONE_VERSION 0xa03 |
|
|
|
|
|
|
|
/* 0 - UP, !0 - DOWN */ |
|
|
|
static int32_t pm_down = !0; |
|
|
|
/* !0 - UP, 0 - DOWN */ |
|
|
|
static int32_t pm_up = 0; |
|
|
|
|
|
|
|
#if ZYNQMP_WDT_RESTART |
|
|
|
static spinlock_t inc_lock; |
|
|
|
static int active_cores = 0; |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* pm_context - Structure which contains data for power management |
|
|
@ -35,6 +46,142 @@ static struct { |
|
|
|
uint32_t payload[PAYLOAD_ARG_CNT]; |
|
|
|
} pm_ctx; |
|
|
|
|
|
|
|
#if ZYNQMP_WDT_RESTART |
|
|
|
/**
|
|
|
|
* trigger_wdt_restart() - Trigger warm restart event to APU cores |
|
|
|
* |
|
|
|
* This function triggers SGI for all active APU CPUs. SGI handler then |
|
|
|
* power down CPU and call system reset. |
|
|
|
*/ |
|
|
|
static void trigger_wdt_restart(void) |
|
|
|
{ |
|
|
|
uint32_t core_count = 0; |
|
|
|
uint32_t core_status[3]; |
|
|
|
uint32_t target_cpu_list = 0; |
|
|
|
int i; |
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) { |
|
|
|
pm_get_node_status(NODE_APU_0 + i, core_status); |
|
|
|
if (core_status[0] == 1) { |
|
|
|
core_count++; |
|
|
|
target_cpu_list |= (1 << i); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
spin_lock(&inc_lock); |
|
|
|
active_cores = core_count; |
|
|
|
spin_unlock(&inc_lock); |
|
|
|
|
|
|
|
INFO("Active Cores: %d\n", active_cores); |
|
|
|
|
|
|
|
/* trigger SGI to active cores */ |
|
|
|
gicv2_raise_sgi(ARM_IRQ_SEC_SGI_7, target_cpu_list); |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* ttc_fiq_handler() - TTC Handler for timer event |
|
|
|
* @id number of the highest priority pending interrupt of the type |
|
|
|
* that this handler was registered for |
|
|
|
* @flags security state, bit[0] |
|
|
|
* @handler pointer to 'cpu_context' structure of the current CPU for the |
|
|
|
* security state specified in the 'flags' parameter |
|
|
|
* @cookie unused |
|
|
|
* |
|
|
|
* Function registered as INTR_TYPE_EL3 interrupt handler |
|
|
|
* |
|
|
|
* When WDT event is received in PMU, PMU needs to notify master to do cleanup |
|
|
|
* if required. PMU sets up timer and starts timer to overflow in zero time upon |
|
|
|
* WDT event. ATF handles this timer event and takes necessary action required |
|
|
|
* for warm restart. |
|
|
|
* |
|
|
|
* In presence of non-secure software layers (EL1/2) sets the interrupt |
|
|
|
* at registered entrance in GIC and informs that PMU responsed or demands |
|
|
|
* action. |
|
|
|
*/ |
|
|
|
static uint64_t ttc_fiq_handler(uint32_t id, uint32_t flags, void *handle, |
|
|
|
void *cookie) |
|
|
|
{ |
|
|
|
INFO("BL31: Got TTC FIQ\n"); |
|
|
|
|
|
|
|
/* Clear TTC interrupt by reading interrupt register */ |
|
|
|
mmio_read_32(TTC3_INTR_REGISTER_1); |
|
|
|
|
|
|
|
/* Disable the timer interrupts */ |
|
|
|
mmio_write_32(TTC3_INTR_ENABLE_1, 0); |
|
|
|
|
|
|
|
trigger_wdt_restart(); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* zynqmp_sgi7_irq() - Handler for SGI7 IRQ |
|
|
|
* @id number of the highest priority pending interrupt of the type |
|
|
|
* that this handler was registered for |
|
|
|
* @flags security state, bit[0] |
|
|
|
* @handler pointer to 'cpu_context' structure of the current CPU for the |
|
|
|
* security state specified in the 'flags' parameter |
|
|
|
* @cookie unused |
|
|
|
* |
|
|
|
* Function registered as INTR_TYPE_EL3 interrupt handler |
|
|
|
* |
|
|
|
* On receiving WDT event from PMU, ATF generates SGI7 to all running CPUs. |
|
|
|
* In response to SGI7 interrupt, each CPUs do clean up if required and last |
|
|
|
* running CPU calls system restart. |
|
|
|
*/ |
|
|
|
static uint64_t __unused __dead2 zynqmp_sgi7_irq(uint32_t id, uint32_t flags, |
|
|
|
void *handle, void *cookie) |
|
|
|
{ |
|
|
|
int i; |
|
|
|
/* enter wfi and stay there */ |
|
|
|
INFO("Entering wfi\n"); |
|
|
|
|
|
|
|
spin_lock(&inc_lock); |
|
|
|
active_cores--; |
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) { |
|
|
|
mmio_write_32(BASE_GICD_BASE + GICD_CPENDSGIR + 4 * i, |
|
|
|
0xffffffff); |
|
|
|
} |
|
|
|
|
|
|
|
spin_unlock(&inc_lock); |
|
|
|
|
|
|
|
if (active_cores == 0) { |
|
|
|
pm_system_shutdown(PMF_SHUTDOWN_TYPE_RESET, |
|
|
|
PMF_SHUTDOWN_SUBTYPE_SUBSYSTEM); |
|
|
|
} |
|
|
|
|
|
|
|
/* enter wfi and stay there */ |
|
|
|
while (1) |
|
|
|
wfi(); |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* pm_wdt_restart_setup() - Setup warm restart interrupts |
|
|
|
* |
|
|
|
* This function sets up handler for SGI7 and TTC interrupts |
|
|
|
* used for warm restart. |
|
|
|
*/ |
|
|
|
static int pm_wdt_restart_setup(void) |
|
|
|
{ |
|
|
|
int ret; |
|
|
|
|
|
|
|
/* register IRQ handler for SGI7 */ |
|
|
|
ret = request_intr_type_el3(ARM_IRQ_SEC_SGI_7, zynqmp_sgi7_irq); |
|
|
|
if (ret) { |
|
|
|
WARN("BL31: registering SGI7 interrupt failed\n"); |
|
|
|
goto err; |
|
|
|
} |
|
|
|
|
|
|
|
ret = request_intr_type_el3(IRQ_TTC3_1, ttc_fiq_handler); |
|
|
|
if (ret) |
|
|
|
WARN("BL31: registering TTC3 interrupt failed\n"); |
|
|
|
|
|
|
|
err: |
|
|
|
return ret; |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
/**
|
|
|
|
* pm_setup() - PM service setup |
|
|
|
* |
|
|
@ -52,11 +199,14 @@ int pm_setup(void) |
|
|
|
{ |
|
|
|
int status, ret; |
|
|
|
|
|
|
|
if (!zynqmp_is_pmu_up()) |
|
|
|
return -ENODEV; |
|
|
|
|
|
|
|
status = pm_ipi_init(primary_proc); |
|
|
|
|
|
|
|
#if ZYNQMP_WDT_RESTART |
|
|
|
status = pm_wdt_restart_setup(); |
|
|
|
if (status) |
|
|
|
WARN("BL31: warm-restart setup failed\n"); |
|
|
|
#endif |
|
|
|
|
|
|
|
if (status >= 0) { |
|
|
|
INFO("BL31: PM Service Init Complete: API v%d.%d\n", |
|
|
|
PM_VERSION_MAJOR, PM_VERSION_MINOR); |
|
|
@ -66,7 +216,7 @@ int pm_setup(void) |
|
|
|
ret = status; |
|
|
|
} |
|
|
|
|
|
|
|
pm_down = status; |
|
|
|
pm_up = !status; |
|
|
|
|
|
|
|
return ret; |
|
|
|
} |
|
|
@ -95,7 +245,7 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, |
|
|
|
uint32_t pm_arg[4]; |
|
|
|
|
|
|
|
/* Handle case where PM wasn't initialized properly */ |
|
|
|
if (pm_down) |
|
|
|
if (!pm_up) |
|
|
|
SMC_RET1(handle, SMC_UNK); |
|
|
|
|
|
|
|
pm_arg[0] = (uint32_t)x1; |
|
|
@ -116,9 +266,16 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, |
|
|
|
SMC_RET1(handle, (uint64_t)ret); |
|
|
|
|
|
|
|
case PM_REQ_WAKEUP: |
|
|
|
ret = pm_req_wakeup(pm_arg[0], pm_arg[1], pm_arg[2], |
|
|
|
{ |
|
|
|
/* Use address flag is encoded in the 1st bit of the low-word */ |
|
|
|
unsigned int set_addr = pm_arg[1] & 0x1; |
|
|
|
uint64_t address = (uint64_t)pm_arg[2] << 32; |
|
|
|
|
|
|
|
address |= pm_arg[1] & (~0x1); |
|
|
|
ret = pm_req_wakeup(pm_arg[0], set_addr, address, |
|
|
|
pm_arg[3]); |
|
|
|
SMC_RET1(handle, (uint64_t)ret); |
|
|
|
} |
|
|
|
|
|
|
|
case PM_FORCE_POWERDOWN: |
|
|
|
ret = pm_force_powerdown(pm_arg[0], pm_arg[1]); |
|
|
@ -175,10 +332,19 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, |
|
|
|
ret = pm_set_configuration(pm_arg[0]); |
|
|
|
SMC_RET1(handle, (uint64_t)ret); |
|
|
|
|
|
|
|
case PM_GET_NODE_STATUS: |
|
|
|
ret = pm_get_node_status(pm_arg[0]); |
|
|
|
case PM_INIT_FINALIZE: |
|
|
|
ret = pm_init_finalize(); |
|
|
|
SMC_RET1(handle, (uint64_t)ret); |
|
|
|
|
|
|
|
case PM_GET_NODE_STATUS: |
|
|
|
{ |
|
|
|
uint32_t buff[3]; |
|
|
|
|
|
|
|
ret = pm_get_node_status(pm_arg[0], buff); |
|
|
|
SMC_RET2(handle, (uint64_t)ret | ((uint64_t)buff[0] << 32), |
|
|
|
(uint64_t)buff[1] | ((uint64_t)buff[2] << 32)); |
|
|
|
} |
|
|
|
|
|
|
|
case PM_GET_OP_CHARACTERISTIC: |
|
|
|
{ |
|
|
|
uint32_t result; |
|
|
@ -239,15 +405,10 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, |
|
|
|
result[1]); |
|
|
|
} |
|
|
|
|
|
|
|
case PM_GET_CALLBACK_DATA: |
|
|
|
{ |
|
|
|
uint32_t result[4]; |
|
|
|
|
|
|
|
pm_get_callbackdata(result, sizeof(result)); |
|
|
|
SMC_RET2(handle, |
|
|
|
(uint64_t)result[0] | ((uint64_t)result[1] << 32), |
|
|
|
(uint64_t)result[2] | ((uint64_t)result[3] << 32)); |
|
|
|
} |
|
|
|
case PM_SECURE_RSA_AES: |
|
|
|
ret = pm_secure_rsaaes(pm_arg[0], pm_arg[1], pm_arg[2], |
|
|
|
pm_arg[3]); |
|
|
|
SMC_RET1(handle, (uint64_t)ret); |
|
|
|
|
|
|
|
case PM_PINCTRL_REQUEST: |
|
|
|
ret = pm_pinctrl_request(pm_arg[0]); |
|
|
@ -361,6 +522,30 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, |
|
|
|
SMC_RET1(handle, (uint64_t)PM_RET_SUCCESS | |
|
|
|
((uint64_t)ZYNQMP_TZ_VERSION << 32)); |
|
|
|
|
|
|
|
case PM_SET_SUSPEND_MODE: |
|
|
|
ret = pm_set_suspend_mode(pm_arg[0]); |
|
|
|
SMC_RET1(handle, (uint64_t)ret); |
|
|
|
|
|
|
|
case PM_SECURE_SHA: |
|
|
|
ret = pm_sha_hash(pm_arg[0], pm_arg[1], pm_arg[2], |
|
|
|
pm_arg[3]); |
|
|
|
SMC_RET1(handle, (uint64_t)ret); |
|
|
|
|
|
|
|
case PM_SECURE_RSA: |
|
|
|
ret = pm_rsa_core(pm_arg[0], pm_arg[1], pm_arg[2], |
|
|
|
pm_arg[3]); |
|
|
|
SMC_RET1(handle, (uint64_t)ret); |
|
|
|
|
|
|
|
case PM_SECURE_IMAGE: |
|
|
|
{ |
|
|
|
uint32_t result[2]; |
|
|
|
|
|
|
|
ret = pm_secure_image(pm_arg[0], pm_arg[1], pm_arg[2], |
|
|
|
pm_arg[3], &result[0]); |
|
|
|
SMC_RET2(handle, (uint64_t)ret | ((uint64_t)result[0] << 32), |
|
|
|
result[1]); |
|
|
|
} |
|
|
|
|
|
|
|
default: |
|
|
|
WARN("Unimplemented PM Service Call: 0x%x\n", smc_fid); |
|
|
|
SMC_RET1(handle, SMC_UNK); |
|
|
|