Browse Source
PSCI framework uses SCPI driver to communicate to SCP firmware for various power management operations. Following PSCI operations are supported: - CPU ON - CPU OFF - CPU STANDBY - SYSTEM RESET - SYSTEM OFF Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org> Signed-off-by: Sumit Garg <sumit.garg@linaro.org>pull/1427/head
Sumit Garg
6 years ago
2 changed files with 198 additions and 0 deletions
@ -0,0 +1,188 @@ |
|||
/*
|
|||
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
|
|||
#include <arch_helpers.h> |
|||
#include <assert.h> |
|||
#include <cassert.h> |
|||
#include <debug.h> |
|||
#include <delay_timer.h> |
|||
#include <errno.h> |
|||
#include <generic_delay_timer.h> |
|||
#include <platform_def.h> |
|||
#include <sq_common.h> |
|||
#include "sq_scpi.h" |
|||
#include <psci.h> |
|||
|
|||
/* Macros to read the SQ power domain state */ |
|||
#define SQ_PWR_LVL0 MPIDR_AFFLVL0 |
|||
#define SQ_PWR_LVL1 MPIDR_AFFLVL1 |
|||
#define SQ_PWR_LVL2 MPIDR_AFFLVL2 |
|||
|
|||
#define SQ_CORE_PWR_STATE(state) (state)->pwr_domain_state[SQ_PWR_LVL0] |
|||
#define SQ_CLUSTER_PWR_STATE(state) (state)->pwr_domain_state[SQ_PWR_LVL1] |
|||
#define SQ_SYSTEM_PWR_STATE(state) ((PLAT_MAX_PWR_LVL > SQ_PWR_LVL1) ?\ |
|||
(state)->pwr_domain_state[SQ_PWR_LVL2] : 0) |
|||
|
|||
uintptr_t sq_sec_entrypoint; |
|||
|
|||
int sq_pwr_domain_on(u_register_t mpidr) |
|||
{ |
|||
/*
|
|||
* SCP takes care of powering up parent power domains so we |
|||
* only need to care about level 0 |
|||
*/ |
|||
scpi_set_sq_power_state(mpidr, scpi_power_on, scpi_power_on, |
|||
scpi_power_on); |
|||
|
|||
return PSCI_E_SUCCESS; |
|||
} |
|||
|
|||
static void sq_pwr_domain_on_finisher_common( |
|||
const psci_power_state_t *target_state) |
|||
{ |
|||
assert(SQ_CORE_PWR_STATE(target_state) == SQ_LOCAL_STATE_OFF); |
|||
|
|||
/*
|
|||
* Perform the common cluster specific operations i.e enable coherency |
|||
* if this cluster was off. |
|||
*/ |
|||
if (SQ_CLUSTER_PWR_STATE(target_state) == SQ_LOCAL_STATE_OFF) |
|||
plat_sq_interconnect_enter_coherency(); |
|||
} |
|||
|
|||
void sq_pwr_domain_on_finish(const psci_power_state_t *target_state) |
|||
{ |
|||
/* Assert that the system power domain need not be initialized */ |
|||
assert(SQ_SYSTEM_PWR_STATE(target_state) == SQ_LOCAL_STATE_RUN); |
|||
|
|||
sq_pwr_domain_on_finisher_common(target_state); |
|||
|
|||
/* Program the gic per-cpu distributor or re-distributor interface */ |
|||
sq_gic_pcpu_init(); |
|||
|
|||
/* Enable the gic cpu interface */ |
|||
sq_gic_cpuif_enable(); |
|||
} |
|||
|
|||
static void sq_power_down_common(const psci_power_state_t *target_state) |
|||
{ |
|||
uint32_t cluster_state = scpi_power_on; |
|||
uint32_t system_state = scpi_power_on; |
|||
|
|||
/* Prevent interrupts from spuriously waking up this cpu */ |
|||
sq_gic_cpuif_disable(); |
|||
|
|||
/* Check if power down at system power domain level is requested */ |
|||
if (SQ_SYSTEM_PWR_STATE(target_state) == SQ_LOCAL_STATE_OFF) |
|||
system_state = scpi_power_retention; |
|||
|
|||
/* Cluster is to be turned off, so disable coherency */ |
|||
if (SQ_CLUSTER_PWR_STATE(target_state) == SQ_LOCAL_STATE_OFF) { |
|||
plat_sq_interconnect_exit_coherency(); |
|||
cluster_state = scpi_power_off; |
|||
} |
|||
|
|||
/*
|
|||
* Ask the SCP to power down the appropriate components depending upon |
|||
* their state. |
|||
*/ |
|||
scpi_set_sq_power_state(read_mpidr_el1(), |
|||
scpi_power_off, |
|||
cluster_state, |
|||
system_state); |
|||
} |
|||
|
|||
void sq_pwr_domain_off(const psci_power_state_t *target_state) |
|||
{ |
|||
sq_power_down_common(target_state); |
|||
} |
|||
|
|||
void __dead2 sq_system_off(void) |
|||
{ |
|||
volatile uint32_t *gpio = (uint32_t *)PLAT_SQ_GPIO_BASE; |
|||
|
|||
/* set PD[9] high to power off the system */ |
|||
gpio[5] |= 0x2; /* set output */ |
|||
gpio[1] |= 0x2; /* set high */ |
|||
dmbst(); |
|||
|
|||
generic_delay_timer_init(); |
|||
|
|||
mdelay(1); |
|||
|
|||
while (1) { |
|||
gpio[1] &= ~0x2; /* set low */ |
|||
dmbst(); |
|||
|
|||
mdelay(1); |
|||
|
|||
gpio[1] |= 0x2; /* set high */ |
|||
dmbst(); |
|||
|
|||
mdelay(100); |
|||
} |
|||
|
|||
wfi(); |
|||
ERROR("SQ System Off: operation not handled.\n"); |
|||
panic(); |
|||
} |
|||
|
|||
void __dead2 sq_system_reset(void) |
|||
{ |
|||
uint32_t response; |
|||
|
|||
/* Send the system reset request to the SCP */ |
|||
response = scpi_sys_power_state(scpi_system_reboot); |
|||
|
|||
if (response != SCP_OK) { |
|||
ERROR("SQ System Reset: SCP error %u.\n", response); |
|||
panic(); |
|||
} |
|||
wfi(); |
|||
ERROR("SQ System Reset: operation not handled.\n"); |
|||
panic(); |
|||
} |
|||
|
|||
void sq_cpu_standby(plat_local_state_t cpu_state) |
|||
{ |
|||
unsigned int scr; |
|||
|
|||
assert(cpu_state == SQ_LOCAL_STATE_RET); |
|||
|
|||
scr = read_scr_el3(); |
|||
/* Enable PhysicalIRQ bit for NS world to wake the CPU */ |
|||
write_scr_el3(scr | SCR_IRQ_BIT); |
|||
isb(); |
|||
dsb(); |
|||
wfi(); |
|||
|
|||
/*
|
|||
* Restore SCR to the original value, synchronisation of scr_el3 is |
|||
* done by eret while el3_exit to save some execution cycles. |
|||
*/ |
|||
write_scr_el3(scr); |
|||
} |
|||
|
|||
const plat_psci_ops_t sq_psci_ops = { |
|||
.pwr_domain_on = sq_pwr_domain_on, |
|||
.pwr_domain_off = sq_pwr_domain_off, |
|||
.pwr_domain_on_finish = sq_pwr_domain_on_finish, |
|||
.cpu_standby = sq_cpu_standby, |
|||
.system_off = sq_system_off, |
|||
.system_reset = sq_system_reset, |
|||
}; |
|||
|
|||
int plat_setup_psci_ops(uintptr_t sec_entrypoint, |
|||
const struct plat_psci_ops **psci_ops) |
|||
{ |
|||
sq_sec_entrypoint = sec_entrypoint; |
|||
flush_dcache_range((uint64_t)&sq_sec_entrypoint, |
|||
sizeof(sq_sec_entrypoint)); |
|||
|
|||
*psci_ops = &sq_psci_ops; |
|||
|
|||
return 0; |
|||
} |
Loading…
Reference in new issue