Juno: Add support for PSCI cpu_suspend api
This patch adds support for the PSCI cpu_suspend api to allow entry into low power states until affinity level 1 i.e. cluster. It mainly ensures that a consolidated power off command which specifies the level to which each affinity level should be powered down is sent to the SCP. Change-Id: I84201f5c06e9c41c9e1fbc6438d12ab5f65ee429
This commit is contained in:
parent
c0481acec1
commit
e726c122b5
|
@ -28,6 +28,7 @@
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <arch_helpers.h>
|
#include <arch_helpers.h>
|
||||||
#include <cci400.h>
|
#include <cci400.h>
|
||||||
#include <platform.h>
|
#include <platform.h>
|
||||||
|
@ -37,6 +38,13 @@
|
||||||
#include "juno_private.h"
|
#include "juno_private.h"
|
||||||
#include "scpi.h"
|
#include "scpi.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/* Align the suspend level to allow per-cpu lockless access */
|
||||||
|
uint32_t state[MPIDR_MAX_AFFLVL] __aligned(CACHE_WRITEBACK_GRANULE);
|
||||||
|
} scp_pstate;
|
||||||
|
|
||||||
|
static scp_pstate target_pstate[PLATFORM_CORE_COUNT];
|
||||||
|
|
||||||
int pm_on(unsigned long mpidr,
|
int pm_on(unsigned long mpidr,
|
||||||
unsigned long sec_entrypoint,
|
unsigned long sec_entrypoint,
|
||||||
unsigned long ns_entrypoint,
|
unsigned long ns_entrypoint,
|
||||||
|
@ -90,12 +98,86 @@ int pm_on_finish(unsigned long mpidr, unsigned int afflvl, unsigned int state)
|
||||||
/* Juno todo: Is this setup only needed after a cold boot? */
|
/* Juno todo: Is this setup only needed after a cold boot? */
|
||||||
gic_pcpu_distif_setup(GICD_BASE);
|
gic_pcpu_distif_setup(GICD_BASE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear the mailbox for each cpu
|
||||||
|
*/
|
||||||
|
unsigned long *mbox = (unsigned long *)(unsigned long)(
|
||||||
|
TRUSTED_MAILBOXES_BASE +
|
||||||
|
(platform_get_core_pos(mpidr) << TRUSTED_MAILBOX_SHIFT)
|
||||||
|
);
|
||||||
|
*mbox = 0;
|
||||||
|
flush_dcache_range((unsigned long)mbox, sizeof(*mbox));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PSCI_E_SUCCESS;
|
return PSCI_E_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Common function called while turning a cpu off or suspending it. It is called
|
||||||
|
* for each affinity level until the target affinity level. It keeps track of
|
||||||
|
* the power state that needs to be programmed through the SCP firmware for each
|
||||||
|
* affinity level. Once the target affinity level is reached it uses the MHU
|
||||||
|
* channel to ask the SCP to perform the power operations for each affinity
|
||||||
|
* level accumulated so far.
|
||||||
|
*
|
||||||
|
* CAUTION: There is no guarantee that caches will remain turned on across calls
|
||||||
|
* to this function as each affinity level is dealt with. So do not write & read
|
||||||
|
* global variables across calls. It will be wise to do flush a write to the
|
||||||
|
* global to prevent unpredictable results.
|
||||||
|
******************************************************************************/
|
||||||
|
static int juno_power_down_common(unsigned long mpidr,
|
||||||
|
unsigned int linear_id,
|
||||||
|
unsigned int cur_afflvl,
|
||||||
|
unsigned int tgt_afflvl,
|
||||||
|
unsigned int state)
|
||||||
|
{
|
||||||
|
/* Record which power state this affinity level is supposed to enter */
|
||||||
|
if (state == PSCI_STATE_OFF) {
|
||||||
|
target_pstate[linear_id].state[cur_afflvl] = scpi_power_off;
|
||||||
|
} else {
|
||||||
|
assert(cur_afflvl != MPIDR_AFFLVL0);
|
||||||
|
target_pstate[linear_id].state[cur_afflvl] = scpi_power_on;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cur_afflvl) {
|
||||||
|
case MPIDR_AFFLVL1:
|
||||||
|
/* Cluster is to be turned off, so disable coherency */
|
||||||
|
cci_disable_coherency(mpidr);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MPIDR_AFFLVL0:
|
||||||
|
/* Turn off intra-cluster coherency */
|
||||||
|
write_cpuectlr(read_cpuectlr() & ~CPUECTLR_SMP_BIT);
|
||||||
|
|
||||||
|
/* Prevent interrupts from spuriously waking up this cpu */
|
||||||
|
gic_cpuif_deactivate(GICC_BASE);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
/*
|
||||||
|
* If this is the target affinity level then we need to ask the SCP
|
||||||
|
* to power down the appropriate components depending upon their state
|
||||||
|
*/
|
||||||
|
if (cur_afflvl == tgt_afflvl) {
|
||||||
|
scpi_set_css_power_state(mpidr,
|
||||||
|
target_pstate[linear_id].state[MPIDR_AFFLVL0],
|
||||||
|
target_pstate[linear_id].state[MPIDR_AFFLVL1],
|
||||||
|
scpi_power_on);
|
||||||
|
|
||||||
|
/* Reset the states */
|
||||||
|
target_pstate[linear_id].state[MPIDR_AFFLVL0] = scpi_power_on;
|
||||||
|
target_pstate[linear_id].state[MPIDR_AFFLVL1] = scpi_power_on;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PSCI_E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Handler called when an affinity instance is about to be turned off. The
|
* Handler called when an affinity instance is about to be turned off. The
|
||||||
* level and mpidr determine the affinity instance. The 'state' arg. allows the
|
* level and mpidr determine the affinity instance. The 'state' arg. allows the
|
||||||
|
@ -109,39 +191,60 @@ int pm_on_finish(unsigned long mpidr, unsigned int afflvl, unsigned int state)
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
int pm_off(unsigned long mpidr, unsigned int afflvl, unsigned int state)
|
int pm_off(unsigned long mpidr, unsigned int afflvl, unsigned int state)
|
||||||
{
|
{
|
||||||
/* We're only interested in power off states */
|
return juno_power_down_common(mpidr,
|
||||||
if (state != PSCI_STATE_OFF)
|
platform_get_core_pos(mpidr),
|
||||||
return PSCI_E_SUCCESS;
|
afflvl,
|
||||||
|
plat_get_max_afflvl(),
|
||||||
|
state);
|
||||||
|
}
|
||||||
|
|
||||||
switch (afflvl) {
|
/*******************************************************************************
|
||||||
case MPIDR_AFFLVL1:
|
* Handler called when an affinity instance is about to be suspended. The
|
||||||
/* Cluster is to be turned off, so disable coherency */
|
* level and mpidr determine the affinity instance. The 'state' arg. allows the
|
||||||
cci_disable_coherency(mpidr);
|
* platform to decide whether the cluster is being turned off and take apt
|
||||||
|
* actions. The 'sec_entrypoint' determines the address in BL3-1 from where
|
||||||
|
* execution should resume.
|
||||||
|
*
|
||||||
|
* CAUTION: There is no guarantee that caches will remain turned on across calls
|
||||||
|
* to this function as each affinity level is dealt with. So do not write & read
|
||||||
|
* global variables across calls. It will be wise to do flush a write to the
|
||||||
|
* global to prevent unpredictable results.
|
||||||
|
******************************************************************************/
|
||||||
|
int pm_suspend(unsigned long mpidr,
|
||||||
|
unsigned long sec_entrypoint,
|
||||||
|
unsigned long ns_entrypoint,
|
||||||
|
unsigned int afflvl,
|
||||||
|
unsigned int state)
|
||||||
|
{
|
||||||
|
uint32_t tgt_afflvl;
|
||||||
|
|
||||||
break;
|
tgt_afflvl = psci_get_suspend_afflvl(mpidr);
|
||||||
|
assert(tgt_afflvl != PSCI_INVALID_DATA);
|
||||||
|
|
||||||
case MPIDR_AFFLVL0:
|
/*
|
||||||
/* Turn off intra-cluster coherency */
|
* Setup mailbox with address for CPU entrypoint when it next powers up
|
||||||
write_cpuectlr(read_cpuectlr() & ~CPUECTLR_SMP_BIT);
|
*/
|
||||||
|
if (afflvl == MPIDR_AFFLVL0) {
|
||||||
/* Prevent interrupts from spuriously waking up this cpu */
|
unsigned long *mbox = (unsigned long *)(unsigned long)(
|
||||||
gic_cpuif_deactivate(GICC_BASE);
|
TRUSTED_MAILBOXES_BASE +
|
||||||
|
(platform_get_core_pos(mpidr) << TRUSTED_MAILBOX_SHIFT)
|
||||||
/*
|
);
|
||||||
* Ask SCP to power down CPU.
|
*mbox = sec_entrypoint;
|
||||||
*
|
flush_dcache_range((unsigned long)mbox, sizeof(*mbox));
|
||||||
* Note, we also ask for cluster power down as well because we
|
|
||||||
* know the SCP will only actually do that if this is the last
|
|
||||||
* CPU going down, and also, that final power down won't happen
|
|
||||||
* until this CPU executes the WFI instruction after the PSCI
|
|
||||||
* framework has done it's thing.
|
|
||||||
*/
|
|
||||||
scpi_set_css_power_state(mpidr, scpi_power_off, scpi_power_off,
|
|
||||||
scpi_power_retention);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return PSCI_E_SUCCESS;
|
return juno_power_down_common(mpidr,
|
||||||
|
platform_get_core_pos(mpidr),
|
||||||
|
afflvl,
|
||||||
|
tgt_afflvl,
|
||||||
|
state);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pm_suspend_finish(unsigned long mpidr,
|
||||||
|
unsigned int afflvl,
|
||||||
|
unsigned int state)
|
||||||
|
{
|
||||||
|
return pm_on_finish(mpidr, afflvl, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
@ -150,7 +253,9 @@ int pm_off(unsigned long mpidr, unsigned int afflvl, unsigned int state)
|
||||||
static const plat_pm_ops_t pm_ops = {
|
static const plat_pm_ops_t pm_ops = {
|
||||||
.affinst_on = pm_on,
|
.affinst_on = pm_on,
|
||||||
.affinst_on_finish = pm_on_finish,
|
.affinst_on_finish = pm_on_finish,
|
||||||
.affinst_off = pm_off
|
.affinst_off = pm_off,
|
||||||
|
.affinst_suspend = pm_suspend,
|
||||||
|
.affinst_suspend_finish = pm_suspend_finish
|
||||||
};
|
};
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
|
Loading…
Reference in New Issue