Tegra: remove support for legacy platform APIs

This patch modifies the Tegra port to support the new platform
APIs so that we can disable the compat layer. This includes
modifications to the power management and platform topology code.

Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
This commit is contained in:
Varun Wadekar 2015-08-07 10:03:00 +05:30
parent 864ab0fd21
commit 71cb26ea5b
12 changed files with 368 additions and 298 deletions

View File

@ -36,9 +36,9 @@
#include <tegra_def.h>
/* Global functions */
.globl platform_is_primary_cpu
.globl platform_get_core_pos
.globl platform_get_entrypoint
.globl plat_is_my_cpu_primary
.globl plat_my_core_pos
.globl plat_get_my_entrypoint
.globl plat_secondary_cold_boot_setup
.globl platform_mem_init
.globl plat_crash_console_init
@ -47,7 +47,7 @@
.globl plat_reset_handler
/* Global variables */
.globl sec_entry_point
.globl tegra_sec_entry_point
.globl ns_image_entrypoint
.globl tegra_bl31_phys_base
@ -115,28 +115,47 @@
.endm
/* -----------------------------------------------------
* int platform_is_primary_cpu(int mpidr);
* unsigned int plat_is_my_cpu_primary(void);
*
* This function checks if this is the Primary CPU
* -----------------------------------------------------
*/
func platform_is_primary_cpu
func plat_is_my_cpu_primary
mrs x0, mpidr_el1
and x0, x0, #(MPIDR_CLUSTER_MASK | MPIDR_CPU_MASK)
cmp x0, #TEGRA_PRIMARY_CPU
cset x0, eq
ret
endfunc platform_is_primary_cpu
endfunc plat_is_my_cpu_primary
/* -----------------------------------------------------
* int platform_get_core_pos(int mpidr);
* unsigned int plat_my_core_pos(void);
*
* With this function: CorePos = CoreId
* result: CorePos = CoreId + (ClusterId << 2)
* -----------------------------------------------------
*/
func platform_get_core_pos
and x0, x0, #MPIDR_CPU_MASK
func plat_my_core_pos
mrs x0, mpidr_el1
and x1, x0, #MPIDR_CPU_MASK
and x0, x0, #MPIDR_CLUSTER_MASK
add x0, x1, x0, LSR #6
ret
endfunc platform_get_core_pos
endfunc plat_my_core_pos
/* -----------------------------------------------------
* unsigned long plat_get_my_entrypoint (void);
*
* Main job of this routine is to distinguish between
* a cold and warm boot. If the tegra_sec_entry_point for
* this CPU is present, then it's a warm boot.
*
* -----------------------------------------------------
*/
func plat_get_my_entrypoint
adr x1, tegra_sec_entry_point
ldr x0, [x1]
ret
endfunc plat_get_my_entrypoint
/* -----------------------------------------------------
* void plat_secondary_cold_boot_setup (void);
@ -151,22 +170,6 @@ func plat_secondary_cold_boot_setup
ret
endfunc plat_secondary_cold_boot_setup
/* -----------------------------------------------------
* void platform_get_entrypoint (unsigned int mpidr);
*
* Main job of this routine is to distinguish between
* a cold and warm boot. If the sec_entry_point for
* this CPU is present, then it's a warm boot.
*
* -----------------------------------------------------
*/
func platform_get_entrypoint
and x0, x0, #MPIDR_CPU_MASK
adr x1, sec_entry_point
ldr x0, [x1, x0, lsl #3]
ret
endfunc platform_get_entrypoint
/* --------------------------------------------------------
* void platform_mem_init (void);
*
@ -336,8 +339,7 @@ restore_oslock:
* Get secure world's entry point and jump to it
* --------------------------------------------------
*/
mrs x0, mpidr_el1
bl platform_get_entrypoint
bl plat_get_my_entrypoint
br x0
endfunc tegra_secure_entrypoint
@ -345,13 +347,11 @@ endfunc tegra_secure_entrypoint
.align 3
/* --------------------------------------------------
* Per-CPU Secure entry point - resume from suspend
* CPU Secure entry point - resume from suspend
* --------------------------------------------------
*/
sec_entry_point:
.rept PLATFORM_CORE_COUNT
tegra_sec_entry_point:
.quad 0
.endr
/* --------------------------------------------------
* NS world's cold boot entry point

View File

@ -51,6 +51,7 @@ BL31_SOURCES += drivers/arm/gic/gic_v2.c \
drivers/delay_timer/delay_timer.c \
drivers/ti/uart/16550_console.S \
plat/common/aarch64/platform_mp_stack.S \
plat/common/aarch64/plat_psci_common.c \
${COMMON_DIR}/aarch64/tegra_helpers.S \
${COMMON_DIR}/drivers/memctrl/memctrl.c \
${COMMON_DIR}/drivers/pmc/pmc.c \

View File

@ -44,35 +44,34 @@
#include <tegra_private.h>
extern uint64_t tegra_bl31_phys_base;
extern uint64_t sec_entry_point[PLATFORM_CORE_COUNT];
static int system_suspended;
extern uint64_t tegra_sec_entry_point;
/*
* The following platform setup functions are weakly defined. They
* provide typical implementations that will be overridden by a SoC.
*/
#pragma weak tegra_soc_prepare_cpu_suspend
#pragma weak tegra_soc_prepare_cpu_on
#pragma weak tegra_soc_prepare_cpu_off
#pragma weak tegra_soc_prepare_cpu_on_finish
#pragma weak tegra_soc_pwr_domain_suspend
#pragma weak tegra_soc_pwr_domain_on
#pragma weak tegra_soc_pwr_domain_off
#pragma weak tegra_soc_pwr_domain_on_finish
#pragma weak tegra_soc_prepare_system_reset
int tegra_soc_prepare_cpu_suspend(unsigned int id, unsigned int afflvl)
int tegra_soc_pwr_domain_suspend(const psci_power_state_t *target_state)
{
return PSCI_E_NOT_SUPPORTED;
}
int tegra_soc_prepare_cpu_on(unsigned long mpidr)
int tegra_soc_pwr_domain_on(u_register_t mpidr)
{
return PSCI_E_SUCCESS;
}
int tegra_soc_prepare_cpu_off(unsigned long mpidr)
int tegra_soc_pwr_domain_off(const psci_power_state_t *target_state)
{
return PSCI_E_SUCCESS;
}
int tegra_soc_prepare_cpu_on_finish(unsigned long mpidr)
int tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state)
{
return PSCI_E_SUCCESS;
}
@ -83,33 +82,25 @@ int tegra_soc_prepare_system_reset(void)
}
/*******************************************************************************
* Track system suspend entry.
******************************************************************************/
void tegra_pm_system_suspend_entry(void)
* This handler is called by the PSCI implementation during the `SYSTEM_SUSPEND`
* call to get the `power_state` parameter. This allows the platform to encode
* the appropriate State-ID field within the `power_state` parameter which can
* be utilized in `pwr_domain_suspend()` to suspend to system affinity level.
******************************************************************************/
void tegra_get_sys_suspend_power_state(psci_power_state_t *req_state)
{
system_suspended = 1;
}
/* lower affinities use PLAT_MAX_OFF_STATE */
for (int i = MPIDR_AFFLVL0; i < PLAT_MAX_PWR_LVL; i++)
req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
/*******************************************************************************
* Track system suspend exit.
******************************************************************************/
void tegra_pm_system_suspend_exit(void)
{
system_suspended = 0;
}
/*******************************************************************************
* Get the system suspend state.
******************************************************************************/
int tegra_system_suspended(void)
{
return system_suspended;
/* max affinity uses system suspend state id */
req_state->pwr_domain_state[PLAT_MAX_PWR_LVL] = PSTATE_ID_SOC_POWERDN;
}
/*******************************************************************************
* Handler called when an affinity instance is about to enter standby.
******************************************************************************/
void tegra_affinst_standby(unsigned int power_state)
void tegra_cpu_standby(plat_local_state_t cpu_state)
{
/*
* Enter standby state
@ -119,132 +110,45 @@ void tegra_affinst_standby(unsigned int power_state)
wfi();
}
/*******************************************************************************
* This handler is called by the PSCI implementation during the `SYSTEM_SUSPEND`
* call to get the `power_state` parameter. This allows the platform to encode
* the appropriate State-ID field within the `power_state` parameter which can
* be utilized in `affinst_suspend()` to suspend to system affinity level.
******************************************************************************/
unsigned int tegra_get_sys_suspend_power_state(void)
{
unsigned int power_state;
power_state = psci_make_powerstate(PLAT_SYS_SUSPEND_STATE_ID,
PSTATE_TYPE_POWERDOWN, MPIDR_AFFLVL2);
return power_state;
}
/*******************************************************************************
* Handler called to check the validity of the power state parameter.
******************************************************************************/
int32_t tegra_validate_power_state(unsigned int power_state)
{
return tegra_soc_validate_power_state(power_state);
}
/*******************************************************************************
* Handler called when an affinity instance is about to be turned on. The
* level and mpidr determine the affinity instance.
******************************************************************************/
int tegra_affinst_on(unsigned long mpidr,
unsigned long sec_entrypoint,
unsigned int afflvl,
unsigned int state)
int tegra_pwr_domain_on(u_register_t mpidr)
{
int cpu = mpidr & MPIDR_CPU_MASK;
/*
* Support individual CPU power on only.
*/
if (afflvl > MPIDR_AFFLVL0)
return PSCI_E_SUCCESS;
/*
* Flush entrypoint variable to PoC since it will be
* accessed after a reset with the caches turned off.
*/
sec_entry_point[cpu] = sec_entrypoint;
flush_dcache_range((uint64_t)&sec_entry_point[cpu], sizeof(uint64_t));
return tegra_soc_prepare_cpu_on(mpidr);
return tegra_soc_pwr_domain_on(mpidr);
}
/*******************************************************************************
* Handler called when an affinity instance is about to be turned off. The
* level determines the affinity instance. The 'state' arg. allows the
* platform to decide whether the cluster is being turned off and take apt
* actions.
*
* CAUTION: This function is called with coherent stacks so that caches can be
* turned off, flushed and coherency disabled. 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.
* Handler called when a power domain is about to be turned off. The
* target_state encodes the power state that each level should transition to.
******************************************************************************/
void tegra_affinst_off(unsigned int afflvl, unsigned int state)
void tegra_pwr_domain_off(const psci_power_state_t *target_state)
{
/*
* Support individual CPU power off only.
*/
if (afflvl > MPIDR_AFFLVL0)
return;
tegra_soc_prepare_cpu_off(read_mpidr());
tegra_soc_pwr_domain_off(target_state);
}
/*******************************************************************************
* Handler called when an affinity instance is about to be suspended. The
* level and mpidr determine the affinity instance. The 'state' arg. allows the
* platform to decide whether the cluster is being turned off and take apt
* actions.
*
* CAUTION: This function is called with coherent stacks so that caches can be
* turned off, flushed and coherency disabled. 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 flush a write to the global variable, to prevent unpredictable
* results.
* Handler called when called when a power domain is about to be suspended. The
* target_state encodes the power state that each level should transition to.
******************************************************************************/
void tegra_affinst_suspend(unsigned long sec_entrypoint,
unsigned int afflvl,
unsigned int state)
void tegra_pwr_domain_suspend(const psci_power_state_t *target_state)
{
int id = psci_get_suspend_stateid();
int cpu = read_mpidr() & MPIDR_CPU_MASK;
if (afflvl > PLATFORM_MAX_AFFLVL)
return;
/*
* Flush entrypoint variable to PoC since it will be
* accessed after a reset with the caches turned off.
*/
sec_entry_point[cpu] = sec_entrypoint;
flush_dcache_range((uint64_t)&sec_entry_point[cpu], sizeof(uint64_t));
tegra_soc_prepare_cpu_suspend(id, afflvl);
tegra_soc_pwr_domain_suspend(target_state);
/* disable GICC */
tegra_gic_cpuif_deactivate();
}
/*******************************************************************************
* Handler called when an affinity instance has just been powered on after
* being turned off earlier. The level determines the affinity instance.
* The 'state' arg. allows the platform to decide whether the cluster was
* turned off prior to wakeup and do what's necessary to set it up.
* Handler called when a power domain has just been powered on after
* being turned off earlier. The target_state encodes the low power state that
* each level has woken up from.
******************************************************************************/
void tegra_affinst_on_finish(unsigned int afflvl, unsigned int state)
void tegra_pwr_domain_on_finish(const psci_power_state_t *target_state)
{
plat_params_from_bl2_t *plat_params;
/*
* Support individual CPU power on only.
*/
if (afflvl > MPIDR_AFFLVL0)
return;
/*
* Initialize the GIC cpu and distributor interfaces
*/
@ -253,7 +157,8 @@ void tegra_affinst_on_finish(unsigned int afflvl, unsigned int state)
/*
* Check if we are exiting from deep sleep.
*/
if (tegra_system_suspended()) {
if (target_state->pwr_domain_state[PLAT_MAX_PWR_LVL] ==
PSTATE_ID_SOC_POWERDN) {
/*
* Lock scratch registers which hold the CPU vectors.
@ -276,18 +181,17 @@ void tegra_affinst_on_finish(unsigned int afflvl, unsigned int state)
/*
* Reset hardware settings.
*/
tegra_soc_prepare_cpu_on_finish(read_mpidr());
tegra_soc_pwr_domain_on_finish(target_state);
}
/*******************************************************************************
* Handler called when an affinity instance has just been powered on after
* having been suspended earlier. The level and mpidr determine the affinity
* instance.
* Handler called when a power domain has just been powered on after
* having been suspended earlier. The target_state encodes the low power state
* that each level has woken up from.
******************************************************************************/
void tegra_affinst_suspend_finish(unsigned int afflvl, unsigned int state)
void tegra_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
{
if (afflvl == MPIDR_AFFLVL0)
tegra_affinst_on_finish(afflvl, state);
tegra_pwr_domain_on_finish(target_state);
}
/*******************************************************************************
@ -313,36 +217,78 @@ __dead2 void tegra_system_reset(void)
tegra_pmc_system_reset();
}
/*******************************************************************************
* Handler called to check the validity of the power state parameter.
******************************************************************************/
int32_t tegra_validate_power_state(unsigned int power_state,
psci_power_state_t *req_state)
{
int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
assert(req_state);
if (pwr_lvl > PLAT_MAX_PWR_LVL)
return PSCI_E_INVALID_PARAMS;
return tegra_soc_validate_power_state(power_state, req_state);
}
/*******************************************************************************
* Platform handler called to check the validity of the non secure entrypoint.
******************************************************************************/
int tegra_validate_ns_entrypoint(uintptr_t entrypoint)
{
/*
* Check if the non secure entrypoint lies within the non
* secure DRAM.
*/
if ((entrypoint >= TEGRA_DRAM_BASE) && (entrypoint <= TEGRA_DRAM_END))
return PSCI_E_SUCCESS;
return PSCI_E_INVALID_ADDRESS;
}
/*******************************************************************************
* Export the platform handlers to enable psci to invoke them
******************************************************************************/
static const plat_pm_ops_t tegra_plat_pm_ops = {
.affinst_standby = tegra_affinst_standby,
.affinst_on = tegra_affinst_on,
.affinst_off = tegra_affinst_off,
.affinst_suspend = tegra_affinst_suspend,
.affinst_on_finish = tegra_affinst_on_finish,
.affinst_suspend_finish = tegra_affinst_suspend_finish,
.system_off = tegra_system_off,
.system_reset = tegra_system_reset,
.validate_power_state = tegra_validate_power_state,
.get_sys_suspend_power_state = tegra_get_sys_suspend_power_state
static const plat_psci_ops_t tegra_plat_psci_ops = {
.cpu_standby = tegra_cpu_standby,
.pwr_domain_on = tegra_pwr_domain_on,
.pwr_domain_off = tegra_pwr_domain_off,
.pwr_domain_suspend = tegra_pwr_domain_suspend,
.pwr_domain_on_finish = tegra_pwr_domain_on_finish,
.pwr_domain_suspend_finish = tegra_pwr_domain_suspend_finish,
.system_off = tegra_system_off,
.system_reset = tegra_system_reset,
.validate_power_state = tegra_validate_power_state,
.validate_ns_entrypoint = tegra_validate_ns_entrypoint,
.get_sys_suspend_power_state = tegra_get_sys_suspend_power_state,
};
/*******************************************************************************
* Export the platform specific power ops & initialize the fvp power controller
* Export the platform specific power ops and initialize Power Controller
******************************************************************************/
int platform_setup_pm(const plat_pm_ops_t **plat_ops)
int plat_setup_psci_ops(uintptr_t sec_entrypoint,
const plat_psci_ops_t **psci_ops)
{
psci_power_state_t target_state = { { PSCI_LOCAL_STATE_RUN } };
/*
* Flush entrypoint variable to PoC since it will be
* accessed after a reset with the caches turned off.
*/
tegra_sec_entry_point = sec_entrypoint;
flush_dcache_range((uint64_t)&tegra_sec_entry_point, sizeof(uint64_t));
/*
* Reset hardware settings.
*/
tegra_soc_prepare_cpu_on_finish(read_mpidr());
tegra_soc_pwr_domain_on_finish(&target_state);
/*
* Initialize PM ops struct
* Initialize PSCI ops struct
*/
*plat_ops = &tegra_plat_pm_ops;
*psci_ops = &tegra_plat_psci_ops;
return 0;
}

View File

@ -28,45 +28,47 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <arch_helpers.h>
#include <arch.h>
#include <platform_def.h>
#include <psci.h>
extern const unsigned char tegra_power_domain_tree_desc[];
/*******************************************************************************
* This function implements a part of the critical interface between the psci
* generic layer and the platform to allow the former to detect the platform
* topology. psci queries the platform to determine how many affinity instances
* are present at a particular level for a given mpidr.
* This function returns the Tegra default topology tree information.
******************************************************************************/
unsigned int plat_get_aff_count(unsigned int aff_lvl,
unsigned long mpidr)
const unsigned char *plat_get_power_domain_tree_desc(void)
{
switch (aff_lvl) {
case MPIDR_AFFLVL2:
/* Last supported affinity level */
return 1;
case MPIDR_AFFLVL1:
/* Return # of clusters */
return PLATFORM_CLUSTER_COUNT;
case MPIDR_AFFLVL0:
/* # of cpus per cluster */
return PLATFORM_MAX_CPUS_PER_CLUSTER;
default:
return PSCI_AFF_ABSENT;
}
return tegra_power_domain_tree_desc;
}
/*******************************************************************************
* This function implements a part of the critical interface between the psci
* generic layer and the platform to allow the former to detect the state of a
* affinity instance in the platform topology. psci queries the platform to
* determine whether an affinity instance is present or absent.
* generic layer and the platform that allows the former to query the platform
* to convert an MPIDR to a unique linear index. An error code (-1) is returned
* in case the MPIDR is invalid.
******************************************************************************/
unsigned int plat_get_aff_state(unsigned int aff_lvl,
unsigned long mpidr)
int plat_core_pos_by_mpidr(u_register_t mpidr)
{
return (aff_lvl <= MPIDR_AFFLVL2) ? PSCI_AFF_PRESENT : PSCI_AFF_ABSENT;
unsigned int cluster_id, cpu_id;
mpidr &= MPIDR_AFFINITY_MASK;
if (mpidr & ~(MPIDR_CLUSTER_MASK | MPIDR_CPU_MASK))
return -1;
cluster_id = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK;
cpu_id = (mpidr >> MPIDR_AFF0_SHIFT) & MPIDR_AFFLVL_MASK;
if (cluster_id >= PLATFORM_CLUSTER_COUNT)
return -1;
/*
* Validate cpu_id by checking whether it represents a CPU in
* one of the two clusters present on the platform.
*/
if (cpu_id >= PLATFORM_MAX_CPUS_PER_CLUSTER)
return -1;
return (cpu_id + (cluster_id * 4));
}

View File

@ -33,6 +33,7 @@
#include <arch.h>
#include <common_def.h>
#include <tegra_def.h>
/*******************************************************************************
* Generic platform constants
@ -47,12 +48,18 @@
#define TEGRA_PRIMARY_CPU 0x0
#define PLATFORM_MAX_AFFLVL MPIDR_AFFLVL2
#define PLAT_MAX_PWR_LVL MPIDR_AFFLVL2
#define PLATFORM_CORE_COUNT (PLATFORM_CLUSTER_COUNT * \
PLATFORM_MAX_CPUS_PER_CLUSTER)
#define PLATFORM_NUM_AFFS (PLATFORM_CORE_COUNT + \
#define PLAT_NUM_PWR_DOMAINS (PLATFORM_CORE_COUNT + \
PLATFORM_CLUSTER_COUNT + 1)
/*******************************************************************************
* Platform power states
******************************************************************************/
#define PLAT_MAX_RET_STATE 1
#define PLAT_MAX_OFF_STATE (PSTATE_ID_SOC_POWERDN + 1)
/*******************************************************************************
* Platform console related constants
******************************************************************************/

View File

@ -37,7 +37,7 @@
* This value is used by the PSCI implementation during the `SYSTEM_SUSPEND`
* call as the `state-id` field in the 'power state' parameter.
******************************************************************************/
#define PLAT_SYS_SUSPEND_STATE_ID 0xD
#define PSTATE_ID_SOC_POWERDN 0xD
/*******************************************************************************
* GIC memory map

View File

@ -31,8 +31,9 @@
#ifndef __TEGRA_PRIVATE_H__
#define __TEGRA_PRIVATE_H__
#include <xlat_tables.h>
#include <arch.h>
#include <platform_def.h>
#include <xlat_tables.h>
/*******************************************************************************
* Tegra DRAM memory base address
@ -45,7 +46,8 @@ typedef struct plat_params_from_bl2 {
} plat_params_from_bl2_t;
/* Declarations for plat_psci_handlers.c */
int32_t tegra_soc_validate_power_state(unsigned int power_state);
int32_t tegra_soc_validate_power_state(unsigned int power_state,
psci_power_state_t *req_state);
/* Declarations for plat_setup.c */
const mmap_region_t *plat_get_mmio_map(void);

View File

@ -28,7 +28,10 @@
# POSSIBILITY OF SUCH DAMAGE.
#
SOC_DIR := plat/nvidia/tegra/soc/${TARGET_SOC}
SOC_DIR := plat/nvidia/tegra/soc/${TARGET_SOC}
# Disable the PSCI platform compatibility layer
ENABLE_PLAT_COMPAT := 0
include plat/nvidia/tegra/common/tegra_common.mk
include ${SOC_DIR}/platform_${TARGET_SOC}.mk

View File

@ -56,28 +56,55 @@
static int cpu_powergate_mask[PLATFORM_MAX_CPUS_PER_CLUSTER];
int32_t tegra_soc_validate_power_state(unsigned int power_state)
int32_t tegra_soc_validate_power_state(unsigned int power_state,
psci_power_state_t *req_state)
{
int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
int state_id = psci_get_pstate_id(power_state);
int cpu = read_mpidr() & MPIDR_CPU_MASK;
if (pwr_lvl > PLAT_MAX_PWR_LVL)
return PSCI_E_INVALID_PARAMS;
/* Sanity check the requested afflvl */
if (psci_get_pstate_type(power_state) == PSTATE_TYPE_STANDBY) {
/*
* It's possible to enter standby only on affinity level 0 i.e.
* a cpu on Tegra. Ignore any other affinity level.
*/
if (psci_get_pstate_afflvl(power_state) != MPIDR_AFFLVL0)
if (pwr_lvl != MPIDR_AFFLVL0)
return PSCI_E_INVALID_PARAMS;
/* power domain in standby state */
req_state->pwr_domain_state[pwr_lvl] = PLAT_MAX_RET_STATE;
return PSCI_E_SUCCESS;
}
/* Sanity check the requested state id */
if (psci_get_pstate_id(power_state) != PLAT_SYS_SUSPEND_STATE_ID) {
ERROR("unsupported state id\n");
return PSCI_E_NOT_SUPPORTED;
/*
* Sanity check the requested state id, power level and CPU number.
* Currently T132 only supports SYSTEM_SUSPEND on last standing CPU
* i.e. CPU 0
*/
if ((pwr_lvl != PLAT_MAX_PWR_LVL) ||
(state_id != PSTATE_ID_SOC_POWERDN) ||
(cpu != 0)) {
ERROR("unsupported state id @ power level\n");
return PSCI_E_INVALID_PARAMS;
}
/* Set lower power states to PLAT_MAX_OFF_STATE */
for (int i = MPIDR_AFFLVL0; i < PLAT_MAX_PWR_LVL; i++)
req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
/* Set the SYSTEM_SUSPEND state-id */
req_state->pwr_domain_state[PLAT_MAX_PWR_LVL] =
PSTATE_ID_SOC_POWERDN;
return PSCI_E_SUCCESS;
}
int tegra_soc_prepare_cpu_on(unsigned long mpidr)
int tegra_soc_pwr_domain_on(u_register_t mpidr)
{
int cpu = mpidr & MPIDR_CPU_MASK;
uint32_t mask = CPU_CORE_RESET_MASK << cpu;
@ -101,29 +128,29 @@ int tegra_soc_prepare_cpu_on(unsigned long mpidr)
return PSCI_E_SUCCESS;
}
int tegra_soc_prepare_cpu_off(unsigned long mpidr)
int tegra_soc_pwr_domain_off(const psci_power_state_t *target_state)
{
tegra_fc_cpu_off(mpidr & MPIDR_CPU_MASK);
tegra_fc_cpu_off(read_mpidr() & MPIDR_CPU_MASK);
return PSCI_E_SUCCESS;
}
int tegra_soc_prepare_cpu_suspend(unsigned int id, unsigned int afflvl)
int tegra_soc_pwr_domain_suspend(const psci_power_state_t *target_state)
{
/* Nothing to be done for lower affinity levels */
if (afflvl < MPIDR_AFFLVL2)
return PSCI_E_SUCCESS;
#if DEBUG
int cpu = read_mpidr() & MPIDR_CPU_MASK;
/* Enter system suspend state */
tegra_pm_system_suspend_entry();
/* SYSTEM_SUSPEND only on CPU0 */
assert(cpu == 0);
#endif
/* Allow restarting CPU #1 using PMC on suspend exit */
cpu_powergate_mask[1] = 0;
/* Program FC to enter suspend state */
tegra_fc_cpu_idle(read_mpidr());
tegra_fc_cpu_powerdn(read_mpidr());
/* Suspend DCO operations */
write_actlr_el1(id);
write_actlr_el1(target_state->pwr_domain_state[PLAT_MAX_PWR_LVL]);
return PSCI_E_SUCCESS;
}

View File

@ -31,6 +31,21 @@
#include <xlat_tables.h>
#include <tegra_def.h>
/*******************************************************************************
* The Tegra power domain tree has a single system level power domain i.e. a
* single root node. The first entry in the power domain descriptor specifies
* the number of power domains at the highest power level.
*******************************************************************************
*/
const unsigned char tegra_power_domain_tree_desc[] = {
/* No of root nodes */
1,
/* No of clusters */
PLATFORM_CLUSTER_COUNT,
/* No of CPU cores */
PLATFORM_CORE_COUNT,
};
/* sets of MMIO ranges setup */
#define MMIO_RANGE_0_ADDR 0x50000000
#define MMIO_RANGE_1_ADDR 0x60000000

View File

@ -55,83 +55,139 @@
static int cpu_powergate_mask[PLATFORM_MAX_CPUS_PER_CLUSTER];
int32_t tegra_soc_validate_power_state(unsigned int power_state)
int32_t tegra_soc_validate_power_state(unsigned int power_state,
psci_power_state_t *req_state)
{
int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
int state_id = psci_get_pstate_id(power_state);
if (pwr_lvl > PLAT_MAX_PWR_LVL) {
ERROR("%s: unsupported power_state (0x%x)\n", __func__,
power_state);
return PSCI_E_INVALID_PARAMS;
}
/* Sanity check the requested afflvl */
if (psci_get_pstate_type(power_state) == PSTATE_TYPE_STANDBY) {
/*
* It's possible to enter standby only on affinity level 0 i.e.
* a cpu on Tegra. Ignore any other affinity level.
*/
if (psci_get_pstate_afflvl(power_state) != MPIDR_AFFLVL0)
if (pwr_lvl != MPIDR_AFFLVL0)
return PSCI_E_INVALID_PARAMS;
/* power domain in standby state */
req_state->pwr_domain_state[pwr_lvl] = PLAT_MAX_RET_STATE;
return PSCI_E_SUCCESS;
}
/* Sanity check the requested state id */
switch (psci_get_pstate_id(power_state)) {
switch (state_id) {
case PSTATE_ID_CORE_POWERDN:
/*
* Core powerdown request only for afflvl 0
*/
if (pwr_lvl != MPIDR_AFFLVL0)
goto error;
req_state->pwr_domain_state[MPIDR_AFFLVL0] = state_id & 0xff;
break;
case PSTATE_ID_CLUSTER_IDLE:
case PSTATE_ID_CLUSTER_POWERDN:
/*
* Cluster powerdown/idle request only for afflvl 1
*/
if (pwr_lvl != MPIDR_AFFLVL1)
goto error;
req_state->pwr_domain_state[MPIDR_AFFLVL1] = state_id;
req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE;
break;
case PSTATE_ID_SOC_POWERDN:
/*
* System powerdown request only for afflvl 2
*/
if (pwr_lvl != PLAT_MAX_PWR_LVL)
goto error;
for (int i = MPIDR_AFFLVL0; i < PLAT_MAX_PWR_LVL; i++)
req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
req_state->pwr_domain_state[PLAT_MAX_PWR_LVL] =
PLAT_SYS_SUSPEND_STATE_ID;
break;
default:
ERROR("unsupported state id\n");
ERROR("%s: unsupported state id (%d)\n", __func__, state_id);
return PSCI_E_INVALID_PARAMS;
}
return PSCI_E_SUCCESS;
error:
ERROR("%s: unsupported state id (%d)\n", __func__, state_id);
return PSCI_E_INVALID_PARAMS;
}
int tegra_soc_pwr_domain_suspend(const psci_power_state_t *target_state)
{
u_register_t mpidr = read_mpidr();
const plat_local_state_t *pwr_domain_state =
target_state->pwr_domain_state;
unsigned int stateid_afflvl2 = pwr_domain_state[MPIDR_AFFLVL2];
unsigned int stateid_afflvl1 = pwr_domain_state[MPIDR_AFFLVL1];
unsigned int stateid_afflvl0 = pwr_domain_state[MPIDR_AFFLVL0];
if (stateid_afflvl2 == PSTATE_ID_SOC_POWERDN) {
assert(stateid_afflvl0 == PLAT_MAX_OFF_STATE);
assert(stateid_afflvl1 == PLAT_MAX_OFF_STATE);
/* suspend the entire soc */
tegra_fc_soc_powerdn(mpidr);
} else if (stateid_afflvl1 == PSTATE_ID_CLUSTER_IDLE) {
assert(stateid_afflvl0 == PLAT_MAX_OFF_STATE);
/* Prepare for cluster idle */
tegra_fc_cluster_idle(mpidr);
} else if (stateid_afflvl1 == PSTATE_ID_CLUSTER_POWERDN) {
assert(stateid_afflvl0 == PLAT_MAX_OFF_STATE);
/* Prepare for cluster powerdn */
tegra_fc_cluster_powerdn(mpidr);
} else if (stateid_afflvl0 == PSTATE_ID_CORE_POWERDN) {
/* Prepare for cpu powerdn */
tegra_fc_cpu_powerdn(mpidr);
} else {
ERROR("%s: Unknown state id\n", __func__);
return PSCI_E_NOT_SUPPORTED;
}
return PSCI_E_SUCCESS;
}
int tegra_soc_prepare_cpu_suspend(unsigned int id, unsigned int afflvl)
{
/* There's nothing to be done for affinity level 1 */
if (afflvl == MPIDR_AFFLVL1)
return PSCI_E_SUCCESS;
switch (id) {
/* Prepare for cpu idle */
case PSTATE_ID_CORE_POWERDN:
tegra_fc_cpu_idle(read_mpidr());
return PSCI_E_SUCCESS;
/* Prepare for cluster idle */
case PSTATE_ID_CLUSTER_IDLE:
tegra_fc_cluster_idle(read_mpidr());
return PSCI_E_SUCCESS;
/* Prepare for cluster powerdn */
case PSTATE_ID_CLUSTER_POWERDN:
tegra_fc_cluster_powerdn(read_mpidr());
return PSCI_E_SUCCESS;
/* Prepare for system idle */
case PSTATE_ID_SOC_POWERDN:
/* Enter system suspend state */
tegra_pm_system_suspend_entry();
/* suspend the entire soc */
tegra_fc_soc_powerdn(read_mpidr());
return PSCI_E_SUCCESS;
default:
ERROR("Unknown state id (%d)\n", id);
break;
}
return PSCI_E_NOT_SUPPORTED;
}
int tegra_soc_prepare_cpu_on_finish(unsigned long mpidr)
int tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state)
{
uint32_t val;
/*
* Check if we are exiting from SOC_POWERDN.
*/
if (tegra_system_suspended()) {
if (target_state->pwr_domain_state[PLAT_MAX_PWR_LVL] ==
PLAT_SYS_SUSPEND_STATE_ID) {
/*
* Enable WRAP to INCR burst type conversions for
@ -147,11 +203,6 @@ int tegra_soc_prepare_cpu_on_finish(unsigned long mpidr)
* address and reset it.
*/
tegra_fc_reset_bpmp();
/*
* System resume complete.
*/
tegra_pm_system_suspend_exit();
}
/*
@ -159,13 +210,12 @@ int tegra_soc_prepare_cpu_on_finish(unsigned long mpidr)
* used for power management and boot purposes. Inform the BPMP that
* we have completed the cluster power up.
*/
if (psci_get_max_phys_off_afflvl() == MPIDR_AFFLVL1)
tegra_fc_lock_active_cluster();
tegra_fc_lock_active_cluster();
return PSCI_E_SUCCESS;
}
int tegra_soc_prepare_cpu_on(unsigned long mpidr)
int tegra_soc_pwr_domain_on(u_register_t mpidr)
{
int cpu = mpidr & MPIDR_CPU_MASK;
uint32_t mask = CPU_CORE_RESET_MASK << cpu;
@ -184,9 +234,9 @@ int tegra_soc_prepare_cpu_on(unsigned long mpidr)
return PSCI_E_SUCCESS;
}
int tegra_soc_prepare_cpu_off(unsigned long mpidr)
int tegra_soc_pwr_domain_off(const psci_power_state_t *target_state)
{
tegra_fc_cpu_off(mpidr & MPIDR_CPU_MASK);
tegra_fc_cpu_off(read_mpidr() & MPIDR_CPU_MASK);
return PSCI_E_SUCCESS;
}

View File

@ -32,6 +32,23 @@
#include <tegra_def.h>
#include <xlat_tables.h>
/*******************************************************************************
* The Tegra power domain tree has a single system level power domain i.e. a
* single root node. The first entry in the power domain descriptor specifies
* the number of power domains at the highest power level.
*******************************************************************************
*/
const unsigned char tegra_power_domain_tree_desc[] = {
/* No of root nodes */
1,
/* No of clusters */
PLATFORM_CLUSTER_COUNT,
/* No of CPU cores - cluster0 */
PLATFORM_MAX_CPUS_PER_CLUSTER,
/* No of CPU cores - cluster1 */
PLATFORM_MAX_CPUS_PER_CLUSTER
};
/* sets of MMIO ranges setup */
#define MMIO_RANGE_0_ADDR 0x50000000
#define MMIO_RANGE_1_ADDR 0x60000000