diff --git a/plat/nvidia/tegra/common/aarch64/tegra_helpers.S b/plat/nvidia/tegra/common/aarch64/tegra_helpers.S index d9f287c..a4caf5e 100644 --- a/plat/nvidia/tegra/common/aarch64/tegra_helpers.S +++ b/plat/nvidia/tegra/common/aarch64/tegra_helpers.S @@ -36,9 +36,9 @@ #include /* 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 @@ 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 @@ * 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 @@ .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 diff --git a/plat/nvidia/tegra/common/tegra_common.mk b/plat/nvidia/tegra/common/tegra_common.mk index e1c0d84..fcebde3 100644 --- a/plat/nvidia/tegra/common/tegra_common.mk +++ b/plat/nvidia/tegra/common/tegra_common.mk @@ -51,6 +51,7 @@ 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 \ diff --git a/plat/nvidia/tegra/common/tegra_pm.c b/plat/nvidia/tegra/common/tegra_pm.c index c2c73f6..6fb3e9c 100644 --- a/plat/nvidia/tegra/common/tegra_pm.c +++ b/plat/nvidia/tegra/common/tegra_pm.c @@ -44,35 +44,34 @@ #include 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 @@ } /******************************************************************************* - * 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 @@ -120,132 +111,45 @@ } /******************************************************************************* - * 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 */ tegra_gic_setup(); @@ -253,7 +157,8 @@ /* * 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 @@ /* * 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); } /******************************************************************************* @@ -314,35 +218,77 @@ } /******************************************************************************* + * 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; } diff --git a/plat/nvidia/tegra/common/tegra_topology.c b/plat/nvidia/tegra/common/tegra_topology.c index 220e697..0431d98 100644 --- a/plat/nvidia/tegra/common/tegra_topology.c +++ b/plat/nvidia/tegra/common/tegra_topology.c @@ -28,45 +28,47 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include +#include #include #include +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)); } diff --git a/plat/nvidia/tegra/include/platform_def.h b/plat/nvidia/tegra/include/platform_def.h index c59e2be..2a7935f 100644 --- a/plat/nvidia/tegra/include/platform_def.h +++ b/plat/nvidia/tegra/include/platform_def.h @@ -33,6 +33,7 @@ #include #include +#include /******************************************************************************* * Generic platform constants @@ -47,13 +48,19 @@ #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 ******************************************************************************/ #define TEGRA_CONSOLE_BAUDRATE 115200 diff --git a/plat/nvidia/tegra/include/t132/tegra_def.h b/plat/nvidia/tegra/include/t132/tegra_def.h index 2fb9ed7..683c903 100644 --- a/plat/nvidia/tegra/include/t132/tegra_def.h +++ b/plat/nvidia/tegra/include/t132/tegra_def.h @@ -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 diff --git a/plat/nvidia/tegra/include/tegra_private.h b/plat/nvidia/tegra/include/tegra_private.h index 952e2d8..aca9db7 100644 --- a/plat/nvidia/tegra/include/tegra_private.h +++ b/plat/nvidia/tegra/include/tegra_private.h @@ -31,8 +31,9 @@ #ifndef __TEGRA_PRIVATE_H__ #define __TEGRA_PRIVATE_H__ -#include +#include #include +#include /******************************************************************************* * Tegra DRAM memory base address @@ -45,7 +46,8 @@ } 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); diff --git a/plat/nvidia/tegra/platform.mk b/plat/nvidia/tegra/platform.mk index b909335..cec7caf 100644 --- a/plat/nvidia/tegra/platform.mk +++ b/plat/nvidia/tegra/platform.mk @@ -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 diff --git a/plat/nvidia/tegra/soc/t132/plat_psci_handlers.c b/plat/nvidia/tegra/soc/t132/plat_psci_handlers.c index 46e5940..48a2fba 100644 --- a/plat/nvidia/tegra/soc/t132/plat_psci_handlers.c +++ b/plat/nvidia/tegra/soc/t132/plat_psci_handlers.c @@ -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 @@ 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; } diff --git a/plat/nvidia/tegra/soc/t132/plat_setup.c b/plat/nvidia/tegra/soc/t132/plat_setup.c index a76999c..6ff2831 100644 --- a/plat/nvidia/tegra/soc/t132/plat_setup.c +++ b/plat/nvidia/tegra/soc/t132/plat_setup.c @@ -31,6 +31,21 @@ #include #include +/******************************************************************************* + * 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 diff --git a/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c b/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c index 73358d4..b184063 100644 --- a/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c +++ b/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c @@ -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 @@ * address and reset it. */ tegra_fc_reset_bpmp(); - - /* - * System resume complete. - */ - tegra_pm_system_suspend_exit(); } /* @@ -159,13 +210,12 @@ * 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 @@ 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; } diff --git a/plat/nvidia/tegra/soc/t210/plat_setup.c b/plat/nvidia/tegra/soc/t210/plat_setup.c index cbe7a04..3fce8a2 100644 --- a/plat/nvidia/tegra/soc/t210/plat_setup.c +++ b/plat/nvidia/tegra/soc/t210/plat_setup.c @@ -32,6 +32,23 @@ #include #include +/******************************************************************************* + * 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