mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-05-01 08:05:46 +00:00
fvp: Rework when platform actions are performed
This patch reworks FVP port's power management implementation to perform platform actions only when the platform exported hook is invoked for the highest affinity level to enter/exit the OFF state. For example, during a CPU_OFF operation, fvp_affinst_off() is called twice: for affinity level 0 and affinity level 1 (in that order). CPU specific operations are deferred until the next invocation if it is determined through a call to psci_get_max_phys_off_afflvl() that this is CPU is the last in the cluster. Similarly, during power up if the CPU is the first in the cluster, both CPU and cluster specific operations are performed when fvp_affinst_on_finish() is invoked for affinity level 1. Earlier, they were done across the two invocations of the handler. Change-Id: I4288ed3ba1385db36a69cc2e598deb219f209b8a
This commit is contained in:
parent
0a46e2c340
commit
7d2ccfd79d
1 changed files with 154 additions and 181 deletions
|
@ -39,10 +39,95 @@
|
||||||
#include <plat_config.h>
|
#include <plat_config.h>
|
||||||
#include <platform_def.h>
|
#include <platform_def.h>
|
||||||
#include <psci.h>
|
#include <psci.h>
|
||||||
|
#include <errno.h>
|
||||||
#include "drivers/pwrc/fvp_pwrc.h"
|
#include "drivers/pwrc/fvp_pwrc.h"
|
||||||
#include "fvp_def.h"
|
#include "fvp_def.h"
|
||||||
#include "fvp_private.h"
|
#include "fvp_private.h"
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Private FVP function to program the mailbox for a cpu before it is released
|
||||||
|
* from reset.
|
||||||
|
******************************************************************************/
|
||||||
|
static void fvp_program_mailbox(uint64_t mpidr, uint64_t address)
|
||||||
|
{
|
||||||
|
uint64_t linear_id;
|
||||||
|
mailbox_t *fvp_mboxes;
|
||||||
|
|
||||||
|
linear_id = platform_get_core_pos(mpidr);
|
||||||
|
fvp_mboxes = (mailbox_t *)MBOX_BASE;
|
||||||
|
fvp_mboxes[linear_id].value = address;
|
||||||
|
flush_dcache_range((unsigned long) &fvp_mboxes[linear_id],
|
||||||
|
sizeof(unsigned long));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Function which implements the common FVP specific operations to power down a
|
||||||
|
* cpu in response to a CPU_OFF or CPU_SUSPEND request.
|
||||||
|
******************************************************************************/
|
||||||
|
static void fvp_cpu_pwrdwn_common()
|
||||||
|
{
|
||||||
|
uint32_t ectlr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Take this cpu out of intra-cluster coherency if the FVP flavour
|
||||||
|
* supports the SMP bit.
|
||||||
|
*/
|
||||||
|
if (get_plat_config()->flags & CONFIG_CPUECTLR_SMP_BIT) {
|
||||||
|
ectlr = read_cpuectlr();
|
||||||
|
ectlr &= ~CPUECTLR_SMP_BIT;
|
||||||
|
write_cpuectlr(ectlr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prevent interrupts from spuriously waking up this cpu */
|
||||||
|
arm_gic_cpuif_deactivate();
|
||||||
|
|
||||||
|
/* Program the power controller to power off this cpu. */
|
||||||
|
fvp_pwrc_write_ppoffr(read_mpidr_el1());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Function which implements the common FVP specific operations to power down a
|
||||||
|
* cluster in response to a CPU_OFF or CPU_SUSPEND request.
|
||||||
|
******************************************************************************/
|
||||||
|
static void fvp_cluster_pwrdwn_common()
|
||||||
|
{
|
||||||
|
uint64_t mpidr = read_mpidr_el1();
|
||||||
|
|
||||||
|
/* Disable coherency if this cluster is to be turned off */
|
||||||
|
if (get_plat_config()->flags & CONFIG_HAS_CCI)
|
||||||
|
cci_disable_cluster_coherency(mpidr);
|
||||||
|
|
||||||
|
/* Program the power controller to turn the cluster off */
|
||||||
|
fvp_pwrc_write_pcoffr(mpidr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Private FVP function which is used to determine if any platform actions
|
||||||
|
* should be performed for the specified affinity instance given its
|
||||||
|
* state. Nothing needs to be done if the 'state' is not off or if this is not
|
||||||
|
* the highest affinity level which will enter the 'state'.
|
||||||
|
******************************************************************************/
|
||||||
|
static int32_t fvp_do_plat_actions(unsigned int afflvl, unsigned int state)
|
||||||
|
{
|
||||||
|
unsigned int max_phys_off_afflvl;
|
||||||
|
|
||||||
|
assert(afflvl <= MPIDR_AFFLVL1);
|
||||||
|
|
||||||
|
if (state != PSCI_STATE_OFF)
|
||||||
|
return -EAGAIN;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find the highest affinity level which will be suspended and postpone
|
||||||
|
* all the platform specific actions until that level is hit.
|
||||||
|
*/
|
||||||
|
max_phys_off_afflvl = psci_get_max_phys_off_afflvl();
|
||||||
|
assert(max_phys_off_afflvl != PSCI_INVALID_DATA);
|
||||||
|
if (afflvl != max_phys_off_afflvl)
|
||||||
|
return -EAGAIN;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* FVP handler called when an affinity instance is about to enter standby.
|
* FVP handler called when an affinity instance is about to enter standby.
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
@ -81,8 +166,6 @@ int fvp_affinst_on(unsigned long mpidr,
|
||||||
unsigned int state)
|
unsigned int state)
|
||||||
{
|
{
|
||||||
int rc = PSCI_E_SUCCESS;
|
int rc = PSCI_E_SUCCESS;
|
||||||
unsigned long linear_id;
|
|
||||||
mailbox_t *fvp_mboxes;
|
|
||||||
unsigned int psysr;
|
unsigned int psysr;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -90,7 +173,7 @@ int fvp_affinst_on(unsigned long mpidr,
|
||||||
* on the FVP. Ignore any other affinity level.
|
* on the FVP. Ignore any other affinity level.
|
||||||
*/
|
*/
|
||||||
if (afflvl != MPIDR_AFFLVL0)
|
if (afflvl != MPIDR_AFFLVL0)
|
||||||
goto exit;
|
return rc;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ensure that we do not cancel an inflight power off request
|
* Ensure that we do not cancel an inflight power off request
|
||||||
|
@ -103,15 +186,9 @@ int fvp_affinst_on(unsigned long mpidr,
|
||||||
psysr = fvp_pwrc_read_psysr(mpidr);
|
psysr = fvp_pwrc_read_psysr(mpidr);
|
||||||
} while (psysr & PSYSR_AFF_L0);
|
} while (psysr & PSYSR_AFF_L0);
|
||||||
|
|
||||||
linear_id = platform_get_core_pos(mpidr);
|
fvp_program_mailbox(mpidr, sec_entrypoint);
|
||||||
fvp_mboxes = (mailbox_t *)MBOX_BASE;
|
|
||||||
fvp_mboxes[linear_id].value = sec_entrypoint;
|
|
||||||
flush_dcache_range((unsigned long) &fvp_mboxes[linear_id],
|
|
||||||
sizeof(unsigned long));
|
|
||||||
|
|
||||||
fvp_pwrc_write_pponr(mpidr);
|
fvp_pwrc_write_pponr(mpidr);
|
||||||
|
|
||||||
exit:
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,60 +207,21 @@ int fvp_affinst_off(unsigned long mpidr,
|
||||||
unsigned int afflvl,
|
unsigned int afflvl,
|
||||||
unsigned int state)
|
unsigned int state)
|
||||||
{
|
{
|
||||||
int rc = PSCI_E_SUCCESS;
|
/* Determine if any platform actions need to be executed */
|
||||||
unsigned int ectlr;
|
if (fvp_do_plat_actions(afflvl, state) == -EAGAIN)
|
||||||
|
return PSCI_E_SUCCESS;
|
||||||
switch (afflvl) {
|
|
||||||
case MPIDR_AFFLVL1:
|
|
||||||
if (state == PSCI_STATE_OFF) {
|
|
||||||
/*
|
|
||||||
* Disable coherency if this cluster is to be
|
|
||||||
* turned off
|
|
||||||
*/
|
|
||||||
if (get_plat_config()->flags & CONFIG_HAS_CCI)
|
|
||||||
cci_disable_cluster_coherency(mpidr);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Program the power controller to turn the
|
* If execution reaches this stage then this affinity level will be
|
||||||
* cluster off
|
* suspended. Perform at least the cpu specific actions followed the
|
||||||
|
* cluster specific operations if applicable.
|
||||||
*/
|
*/
|
||||||
fvp_pwrc_write_pcoffr(mpidr);
|
fvp_cpu_pwrdwn_common();
|
||||||
|
|
||||||
}
|
if (afflvl != MPIDR_AFFLVL0)
|
||||||
break;
|
fvp_cluster_pwrdwn_common();
|
||||||
|
|
||||||
case MPIDR_AFFLVL0:
|
return PSCI_E_SUCCESS;
|
||||||
if (state == PSCI_STATE_OFF) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Take this cpu out of intra-cluster coherency if
|
|
||||||
* the FVP flavour supports the SMP bit.
|
|
||||||
*/
|
|
||||||
if (get_plat_config()->flags & CONFIG_CPUECTLR_SMP_BIT) {
|
|
||||||
ectlr = read_cpuectlr();
|
|
||||||
ectlr &= ~CPUECTLR_SMP_BIT;
|
|
||||||
write_cpuectlr(ectlr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Prevent interrupts from spuriously waking up
|
|
||||||
* this cpu
|
|
||||||
*/
|
|
||||||
arm_gic_cpuif_deactivate();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Program the power controller to power this
|
|
||||||
* cpu off
|
|
||||||
*/
|
|
||||||
fvp_pwrc_write_ppoffr(mpidr);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
@ -203,69 +241,24 @@ int fvp_affinst_suspend(unsigned long mpidr,
|
||||||
unsigned int afflvl,
|
unsigned int afflvl,
|
||||||
unsigned int state)
|
unsigned int state)
|
||||||
{
|
{
|
||||||
int rc = PSCI_E_SUCCESS;
|
/* Determine if any platform actions need to be executed. */
|
||||||
unsigned int ectlr;
|
if (fvp_do_plat_actions(afflvl, state) == -EAGAIN)
|
||||||
unsigned long linear_id;
|
return PSCI_E_SUCCESS;
|
||||||
mailbox_t *fvp_mboxes;
|
|
||||||
|
|
||||||
switch (afflvl) {
|
|
||||||
case MPIDR_AFFLVL1:
|
|
||||||
if (state == PSCI_STATE_OFF) {
|
|
||||||
/*
|
|
||||||
* Disable coherency if this cluster is to be
|
|
||||||
* turned off
|
|
||||||
*/
|
|
||||||
if (get_plat_config()->flags & CONFIG_HAS_CCI)
|
|
||||||
cci_disable_cluster_coherency(mpidr);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Program the power controller to turn the
|
|
||||||
* cluster off
|
|
||||||
*/
|
|
||||||
fvp_pwrc_write_pcoffr(mpidr);
|
|
||||||
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MPIDR_AFFLVL0:
|
|
||||||
if (state == PSCI_STATE_OFF) {
|
|
||||||
/*
|
|
||||||
* Take this cpu out of intra-cluster coherency if
|
|
||||||
* the FVP flavour supports the SMP bit.
|
|
||||||
*/
|
|
||||||
if (get_plat_config()->flags & CONFIG_CPUECTLR_SMP_BIT) {
|
|
||||||
ectlr = read_cpuectlr();
|
|
||||||
ectlr &= ~CPUECTLR_SMP_BIT;
|
|
||||||
write_cpuectlr(ectlr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Program the jump address for the target cpu */
|
/* Program the jump address for the target cpu */
|
||||||
linear_id = platform_get_core_pos(mpidr);
|
fvp_program_mailbox(read_mpidr_el1(), sec_entrypoint);
|
||||||
fvp_mboxes = (mailbox_t *)MBOX_BASE;
|
|
||||||
fvp_mboxes[linear_id].value = sec_entrypoint;
|
|
||||||
flush_dcache_range((unsigned long) &fvp_mboxes[linear_id],
|
|
||||||
sizeof(unsigned long));
|
|
||||||
|
|
||||||
/*
|
/* Program the power controller to enable wakeup interrupts. */
|
||||||
* Prevent interrupts from spuriously waking up
|
|
||||||
* this cpu
|
|
||||||
*/
|
|
||||||
arm_gic_cpuif_deactivate();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Program the power controller to power this
|
|
||||||
* cpu off and enable wakeup interrupts.
|
|
||||||
*/
|
|
||||||
fvp_pwrc_set_wen(mpidr);
|
fvp_pwrc_set_wen(mpidr);
|
||||||
fvp_pwrc_write_ppoffr(mpidr);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
/* Perform the common cpu specific operations */
|
||||||
assert(0);
|
fvp_cpu_pwrdwn_common();
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
/* Perform the common cluster specific operations */
|
||||||
|
if (afflvl != MPIDR_AFFLVL0)
|
||||||
|
fvp_cluster_pwrdwn_common();
|
||||||
|
|
||||||
|
return PSCI_E_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
@ -280,38 +273,28 @@ int fvp_affinst_on_finish(unsigned long mpidr,
|
||||||
unsigned int state)
|
unsigned int state)
|
||||||
{
|
{
|
||||||
int rc = PSCI_E_SUCCESS;
|
int rc = PSCI_E_SUCCESS;
|
||||||
unsigned long linear_id;
|
|
||||||
mailbox_t *fvp_mboxes;
|
|
||||||
unsigned int ectlr;
|
unsigned int ectlr;
|
||||||
|
|
||||||
switch (afflvl) {
|
/* Determine if any platform actions need to be executed. */
|
||||||
|
if (fvp_do_plat_actions(afflvl, state) == -EAGAIN)
|
||||||
case MPIDR_AFFLVL1:
|
return PSCI_E_SUCCESS;
|
||||||
/* Enable coherency if this cluster was off */
|
|
||||||
if (state == PSCI_STATE_OFF) {
|
|
||||||
|
|
||||||
|
/* Perform the common cluster specific operations */
|
||||||
|
if (afflvl != MPIDR_AFFLVL0) {
|
||||||
/*
|
/*
|
||||||
* This CPU might have woken up whilst the
|
* This CPU might have woken up whilst the cluster was
|
||||||
* cluster was attempting to power down. In
|
* attempting to power down. In this case the FVP power
|
||||||
* this case the FVP power controller will
|
* controller will have a pending cluster power off request
|
||||||
* have a pending cluster power off request
|
* which needs to be cleared by writing to the PPONR register.
|
||||||
* which needs to be cleared by writing to the
|
* This prevents the power controller from interpreting a
|
||||||
* PPONR register. This prevents the power
|
* subsequent entry of this cpu into a simple wfi as a power
|
||||||
* controller from interpreting a subsequent
|
* down request.
|
||||||
* entry of this cpu into a simple wfi as a
|
|
||||||
* power down request.
|
|
||||||
*/
|
*/
|
||||||
fvp_pwrc_write_pponr(mpidr);
|
fvp_pwrc_write_pponr(mpidr);
|
||||||
|
|
||||||
|
/* Enable coherency if this cluster was off */
|
||||||
fvp_cci_enable();
|
fvp_cci_enable();
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case MPIDR_AFFLVL0:
|
|
||||||
/*
|
|
||||||
* Ignore the state passed for a cpu. It could only have
|
|
||||||
* been off if we are here.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Turn on intra-cluster coherency if the FVP flavour supports
|
* Turn on intra-cluster coherency if the FVP flavour supports
|
||||||
|
@ -330,11 +313,7 @@ int fvp_affinst_on_finish(unsigned long mpidr,
|
||||||
fvp_pwrc_clr_wen(mpidr);
|
fvp_pwrc_clr_wen(mpidr);
|
||||||
|
|
||||||
/* Zero the jump address in the mailbox for this cpu */
|
/* Zero the jump address in the mailbox for this cpu */
|
||||||
fvp_mboxes = (mailbox_t *)MBOX_BASE;
|
fvp_program_mailbox(read_mpidr_el1(), 0);
|
||||||
linear_id = platform_get_core_pos(mpidr);
|
|
||||||
fvp_mboxes[linear_id].value = 0;
|
|
||||||
flush_dcache_range((unsigned long) &fvp_mboxes[linear_id],
|
|
||||||
sizeof(unsigned long));
|
|
||||||
|
|
||||||
/* Enable the gic cpu interface */
|
/* Enable the gic cpu interface */
|
||||||
arm_gic_cpuif_setup();
|
arm_gic_cpuif_setup();
|
||||||
|
@ -342,12 +321,6 @@ int fvp_affinst_on_finish(unsigned long mpidr,
|
||||||
/* TODO: This setup is needed only after a cold boot */
|
/* TODO: This setup is needed only after a cold boot */
|
||||||
arm_gic_pcpu_distif_setup();
|
arm_gic_pcpu_distif_setup();
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue