summaryrefslogtreecommitdiff
path: root/plat/nvidia
diff options
context:
space:
mode:
authorVarun Wadekar <vwadekar@nvidia.com>2018-02-27 14:33:57 -0800
committerVarun Wadekar <vwadekar@nvidia.com>2019-01-31 08:48:36 -0800
commit3ca3c27cad16342e5f2b76511aa2e1d9cdb151a6 (patch)
treede6d410e293343f4759f6a198b9b2101ea81ef52 /plat/nvidia
parent93e3b0f34b4e1e2635f1806ada7cd0d71e7b6a87 (diff)
Tegra: support for System Suspend using sc7entry-fw binary
This patch adds support to enter System Suspend on Tegra210 platforms without the traditional BPMP firmware. The BPMP firmware will no longer be supported on Tegra210 platforms and its functionality will be divided across the CPU and sc7entry-fw. The sc7entry-fw takes care of performing the hardware sequence required to enter System Suspend (SC7 power state) from the COP. The CPU is required to load this firmware to the internal RAM of the COP and start the sequence. The CPU also make sure that the COP is off after cold boot and is only powered on when we want to start the actual System Suspend sequence. The previous bootloader loads the firmware to TZDRAM and passes its base and size as part of the boot parameters. The EL3 layer is supposed to sanitize the parameters before touching the firmware blob. To assist the warmboot code with the PMIC discovery, EL3 is also supposed to program PMC's scratch register #210, with appropriate values. Without these settings the warmboot code wont be able to get the device out of System Suspend. Change-Id: I5a7b868512dbfd6cfefd55acf3978a1fd7ebf1e2 Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
Diffstat (limited to 'plat/nvidia')
-rw-r--r--plat/nvidia/tegra/common/drivers/flowctrl/flowctrl.c30
-rw-r--r--plat/nvidia/tegra/common/tegra_bl31_setup.c4
-rw-r--r--plat/nvidia/tegra/include/drivers/flowctrl.h3
-rw-r--r--plat/nvidia/tegra/include/drivers/pmc.h1
-rw-r--r--plat/nvidia/tegra/include/t210/tegra_def.h3
-rw-r--r--plat/nvidia/tegra/include/tegra_private.h4
-rw-r--r--plat/nvidia/tegra/soc/t210/plat_psci_handlers.c70
-rw-r--r--plat/nvidia/tegra/soc/t210/plat_setup.c37
8 files changed, 136 insertions, 16 deletions
diff --git a/plat/nvidia/tegra/common/drivers/flowctrl/flowctrl.c b/plat/nvidia/tegra/common/drivers/flowctrl/flowctrl.c
index 4f89cf46..bdd3ee76 100644
--- a/plat/nvidia/tegra/common/drivers/flowctrl/flowctrl.c
+++ b/plat/nvidia/tegra/common/drivers/flowctrl/flowctrl.c
@@ -257,22 +257,19 @@ void tegra_fc_lock_active_cluster(void)
}
/*******************************************************************************
- * Reset BPMP processor
+ * Power ON BPMP processor
******************************************************************************/
-void tegra_fc_reset_bpmp(void)
+void tegra_fc_bpmp_on(uint32_t entrypoint)
{
- uint32_t val;
-
/* halt BPMP */
tegra_fc_write_32(FLOWCTRL_HALT_BPMP_EVENTS, FLOWCTRL_WAITEVENT);
/* Assert BPMP reset */
mmio_write_32(TEGRA_CAR_RESET_BASE + CLK_RST_DEV_L_SET, CLK_BPMP_RST);
- /* Restore reset address (stored in PMC_SCRATCH39) */
- val = tegra_pmc_read_32(PMC_SCRATCH39);
- mmio_write_32(TEGRA_EVP_BASE + EVP_BPMP_RESET_VECTOR, val);
- while (val != mmio_read_32(TEGRA_EVP_BASE + EVP_BPMP_RESET_VECTOR))
+ /* Set reset address (stored in PMC_SCRATCH39) */
+ mmio_write_32(TEGRA_EVP_BASE + EVP_BPMP_RESET_VECTOR, entrypoint);
+ while (entrypoint != mmio_read_32(TEGRA_EVP_BASE + EVP_BPMP_RESET_VECTOR))
; /* wait till value reaches EVP_BPMP_RESET_VECTOR */
/* Wait for 2us before de-asserting the reset signal. */
@@ -286,6 +283,23 @@ void tegra_fc_reset_bpmp(void)
}
/*******************************************************************************
+ * Power OFF BPMP processor
+ ******************************************************************************/
+void tegra_fc_bpmp_off(void)
+{
+ /* halt BPMP */
+ tegra_fc_write_32(FLOWCTRL_HALT_BPMP_EVENTS, FLOWCTRL_WAITEVENT);
+
+ /* Assert BPMP reset */
+ mmio_write_32(TEGRA_CAR_RESET_BASE + CLK_RST_DEV_L_SET, CLK_BPMP_RST);
+
+ /* Clear reset address */
+ mmio_write_32(TEGRA_EVP_BASE + EVP_BPMP_RESET_VECTOR, 0);
+ while (0 != mmio_read_32(TEGRA_EVP_BASE + EVP_BPMP_RESET_VECTOR))
+ ; /* wait till value reaches EVP_BPMP_RESET_VECTOR */
+}
+
+/*******************************************************************************
* Route legacy FIQ to the GICD
******************************************************************************/
void tegra_fc_enable_fiq_to_ccplex_routing(void)
diff --git a/plat/nvidia/tegra/common/tegra_bl31_setup.c b/plat/nvidia/tegra/common/tegra_bl31_setup.c
index 0d0bc740..c9beb142 100644
--- a/plat/nvidia/tegra/common/tegra_bl31_setup.c
+++ b/plat/nvidia/tegra/common/tegra_bl31_setup.c
@@ -162,13 +162,15 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1,
}
/*
- * Parse platform specific parameters - TZDRAM aperture base and size
+ * Parse platform specific parameters
*/
assert(plat_params != NULL);
plat_bl31_params_from_bl2.tzdram_base = plat_params->tzdram_base;
plat_bl31_params_from_bl2.tzdram_size = plat_params->tzdram_size;
plat_bl31_params_from_bl2.uart_id = plat_params->uart_id;
plat_bl31_params_from_bl2.l2_ecc_parity_prot_dis = plat_params->l2_ecc_parity_prot_dis;
+ plat_bl31_params_from_bl2.sc7entry_fw_size = plat_params->sc7entry_fw_size;
+ plat_bl31_params_from_bl2.sc7entry_fw_base = plat_params->sc7entry_fw_base;
/*
* It is very important that we run either from TZDRAM or TZSRAM base.
diff --git a/plat/nvidia/tegra/include/drivers/flowctrl.h b/plat/nvidia/tegra/include/drivers/flowctrl.h
index bf7e82fb..54336b04 100644
--- a/plat/nvidia/tegra/include/drivers/flowctrl.h
+++ b/plat/nvidia/tegra/include/drivers/flowctrl.h
@@ -77,6 +77,8 @@ static inline void tegra_fc_write_32(uint32_t off, uint32_t val)
mmio_write_32(TEGRA_FLOWCTRL_BASE + off, val);
}
+void tegra_fc_bpmp_on(uint32_t entrypoint);
+void tegra_fc_bpmp_off(void);
void tegra_fc_ccplex_pgexit_lock(void);
void tegra_fc_ccplex_pgexit_unlock(void);
void tegra_fc_cluster_idle(uint32_t midr);
@@ -88,7 +90,6 @@ void tegra_fc_disable_fiq_to_ccplex_routing(void);
void tegra_fc_enable_fiq_to_ccplex_routing(void);
bool tegra_fc_is_ccx_allowed(void);
void tegra_fc_lock_active_cluster(void);
-void tegra_fc_reset_bpmp(void);
void tegra_fc_soc_powerdn(uint32_t midr);
#endif /* FLOWCTRL_H */
diff --git a/plat/nvidia/tegra/include/drivers/pmc.h b/plat/nvidia/tegra/include/drivers/pmc.h
index b9090b4a..53317de1 100644
--- a/plat/nvidia/tegra/include/drivers/pmc.h
+++ b/plat/nvidia/tegra/include/drivers/pmc.h
@@ -26,6 +26,7 @@
#define PMC_SECURE_DISABLE3_WRITE35_ON (U(1) << 22)
#define PMC_SECURE_SCRATCH34 U(0x368)
#define PMC_SECURE_SCRATCH35 U(0x36c)
+#define PMC_SCRATCH201 U(0x844)
static inline uint32_t tegra_pmc_read_32(uint32_t off)
{
diff --git a/plat/nvidia/tegra/include/t210/tegra_def.h b/plat/nvidia/tegra/include/t210/tegra_def.h
index c4ce7670..02c5f135 100644
--- a/plat/nvidia/tegra/include/t210/tegra_def.h
+++ b/plat/nvidia/tegra/include/t210/tegra_def.h
@@ -40,7 +40,8 @@
/*******************************************************************************
* iRAM memory constants
******************************************************************************/
-#define TEGRA_IRAM_BASE 0x40000000
+#define TEGRA_IRAM_BASE U(0x40000000)
+#define TEGRA_IRAM_SIZE U(40000) /* 256KB */
/*******************************************************************************
* GIC memory map
diff --git a/plat/nvidia/tegra/include/tegra_private.h b/plat/nvidia/tegra/include/tegra_private.h
index 40aeaf85..cdd9e08c 100644
--- a/plat/nvidia/tegra/include/tegra_private.h
+++ b/plat/nvidia/tegra/include/tegra_private.h
@@ -46,6 +46,10 @@ typedef struct plat_params_from_bl2 {
int32_t l2_ecc_parity_prot_dis;
/* SHMEM base address for storing the boot logs */
uint64_t boot_profiler_shmem_base;
+ /* System Suspend Entry Firmware size */
+ uint64_t sc7entry_fw_size;
+ /* System Suspend Entry Firmware base address */
+ uint64_t sc7entry_fw_base;
} plat_params_from_bl2_t;
/*******************************************************************************
diff --git a/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c b/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c
index bb3b8fec..4c49386b 100644
--- a/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c
+++ b/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c
@@ -41,6 +41,7 @@ int32_t tegra_soc_validate_power_state(unsigned int power_state,
psci_power_state_t *req_state)
{
int state_id = psci_get_pstate_id(power_state);
+ const plat_params_from_bl2_t *plat_params = bl31_get_plat_params();
/* Sanity check the requested state id */
switch (state_id) {
@@ -62,6 +63,15 @@ int32_t tegra_soc_validate_power_state(unsigned int power_state,
break;
case PSTATE_ID_SOC_POWERDN:
+
+ /*
+ * sc7entry-fw must be present in the system when the bpmp
+ * firmware is not present, for a successful System Suspend
+ * entry.
+ */
+ if (!tegra_bpmp_init() && !plat_params->sc7entry_fw_base)
+ return PSCI_E_NOT_SUPPORTED;
+
/*
* System powerdown request only for afflvl 2
*/
@@ -205,12 +215,26 @@ int tegra_soc_pwr_domain_suspend(const psci_power_state_t *target_state)
if (!tegra_bpmp_available) {
- /* PWM tristate */
+ /* Find if the platform uses OVR2/MAX77621 PMIC */
cfg = mmio_read_32(TEGRA_CL_DVFS_BASE + DVFS_DFLL_OUTPUT_CFG);
if (cfg & DFLL_OUTPUT_CFG_CLK_EN_BIT) {
+ /* OVR2 */
+
+ /* PWM tristate */
val = mmio_read_32(TEGRA_MISC_BASE + PINMUX_AUX_DVFS_PWM);
val |= PINMUX_PWM_TRISTATE;
mmio_write_32(TEGRA_MISC_BASE + PINMUX_AUX_DVFS_PWM, val);
+
+ /*
+ * SCRATCH201[1] is being used to identify CPU
+ * PMIC in warmboot code.
+ * 0 : OVR2
+ * 1 : MAX77621
+ */
+ tegra_pmc_write_32(PMC_SCRATCH201, 0x0);
+ } else {
+ /* MAX77621 */
+ tegra_pmc_write_32(PMC_SCRATCH201, 0x2);
}
}
@@ -237,6 +261,8 @@ int tegra_soc_pwr_domain_power_down_wfi(const psci_power_state_t *target_state)
const plat_local_state_t *pwr_domain_state =
target_state->pwr_domain_state;
unsigned int stateid_afflvl2 = pwr_domain_state[PLAT_MAX_PWR_LVL];
+ const plat_params_from_bl2_t *plat_params = bl31_get_plat_params();
+ uint32_t val;
if (stateid_afflvl2 == PSTATE_ID_SOC_POWERDN) {
@@ -245,6 +271,36 @@ int tegra_soc_pwr_domain_power_down_wfi(const psci_power_state_t *target_state)
tegra_se_save_tzram();
}
+ /*
+ * The CPU needs to load the System suspend entry firmware
+ * if nothing is running on the BPMP.
+ */
+ if (!tegra_bpmp_available) {
+
+ /*
+ * BPMP firmware is not running on the co-processor, so
+ * we need to explicitly load the firmware to enable
+ * entry/exit to/from System Suspend and set the BPMP
+ * on its way.
+ */
+
+ /* Power off BPMP before we proceed */
+ tegra_fc_bpmp_off();
+
+ /* Copy the firmware to BPMP's internal RAM */
+ (void)memcpy((void *)(uintptr_t)TEGRA_IRAM_BASE,
+ (const void *)plat_params->sc7entry_fw_base,
+ plat_params->sc7entry_fw_size);
+
+ /* Power on the BPMP and execute from IRAM base */
+ tegra_fc_bpmp_on(TEGRA_IRAM_BASE);
+
+ /* Wait until BPMP powers up */
+ do {
+ val = mmio_read_32(TEGRA_RES_SEMA_BASE + STA_OFFSET);
+ } while (val != SIGN_OF_LIFE);
+ }
+
/* enter system suspend */
tegra_fc_soc_powerdn(mpidr);
}
@@ -256,7 +312,7 @@ int tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state)
{
const plat_params_from_bl2_t *plat_params = bl31_get_plat_params();
uint32_t cfg;
- uint32_t val;
+ uint32_t val, entrypoint = 0;
/* platform parameter passed by the previous bootloader */
if (plat_params->l2_ecc_parity_prot_dis != 1) {
@@ -295,10 +351,14 @@ int tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state)
/*
* Restore Boot and Power Management Processor (BPMP) reset
- * address and reset it.
+ * address and reset it, if it is supported by the platform.
*/
- if (tegra_bpmp_available)
- tegra_fc_reset_bpmp();
+ if (!tegra_bpmp_available) {
+ tegra_fc_bpmp_off();
+ } else {
+ entrypoint = tegra_pmc_read_32(PMC_SCRATCH39);
+ tegra_fc_bpmp_on(entrypoint);
+ }
}
/*
diff --git a/plat/nvidia/tegra/soc/t210/plat_setup.c b/plat/nvidia/tegra/soc/t210/plat_setup.c
index 25105ba1..4fdd5a8a 100644
--- a/plat/nvidia/tegra/soc/t210/plat_setup.c
+++ b/plat/nvidia/tegra/soc/t210/plat_setup.c
@@ -5,6 +5,7 @@
*/
#include <arch_helpers.h>
+#include <assert.h>
#include <cortex_a57.h>
#include <common/bl_common.h>
#include <common/debug.h>
@@ -150,6 +151,42 @@ static const interrupt_prop_t tegra210_interrupt_props[] = {
GICV2_INTR_GROUP0, GIC_INTR_CFG_EDGE),
};
+void plat_late_platform_setup(void)
+{
+ const plat_params_from_bl2_t *plat_params = bl31_get_plat_params();
+ uint64_t tzdram_start, tzdram_end, sc7entry_end;
+ int ret;
+
+ /* memmap TZDRAM area containing the SC7 Entry Firmware */
+ if (plat_params->sc7entry_fw_base && plat_params->sc7entry_fw_size) {
+
+ assert(plat_params->sc7entry_fw_size <= TEGRA_IRAM_SIZE);
+
+ /*
+ * Verify that the SC7 entry firmware resides inside the TZDRAM
+ * aperture, _after_ the BL31 code.
+ */
+ tzdram_start = plat_params->tzdram_base;
+ tzdram_end = plat_params->tzdram_base + plat_params->tzdram_size;
+ sc7entry_end = plat_params->sc7entry_fw_base +
+ plat_params->sc7entry_fw_size;
+ if ((plat_params->sc7entry_fw_base < (tzdram_start + BL31_SIZE)) ||
+ (sc7entry_end > tzdram_end)) {
+ panic();
+ }
+
+ /* power off BPMP processor until SC7 entry */
+ tegra_fc_bpmp_off();
+
+ /* memmap SC7 entry firmware code */
+ ret = mmap_add_dynamic_region(plat_params->sc7entry_fw_base,
+ plat_params->sc7entry_fw_base,
+ plat_params->sc7entry_fw_size,
+ MT_NS | MT_RO | MT_EXECUTE_NEVER);
+ assert(ret == 0);
+ }
+}
+
/*******************************************************************************
* Initialize the GIC and SGIs
******************************************************************************/