summaryrefslogtreecommitdiff
path: root/plat/freescale/imx8qm/imx8qm_psci.c
diff options
context:
space:
mode:
Diffstat (limited to 'plat/freescale/imx8qm/imx8qm_psci.c')
-rw-r--r--plat/freescale/imx8qm/imx8qm_psci.c240
1 files changed, 240 insertions, 0 deletions
diff --git a/plat/freescale/imx8qm/imx8qm_psci.c b/plat/freescale/imx8qm/imx8qm_psci.c
new file mode 100644
index 00000000..1f038f18
--- /dev/null
+++ b/plat/freescale/imx8qm/imx8qm_psci.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
+ * Copyright 2017 NXP
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <arch.h>
+#include <arch_helpers.h>
+#include <cci.h>
+#include <debug.h>
+#include <plat_imx8.h>
+#include <psci.h>
+#include <sci/sci.h>
+
+extern sc_ipc_t ipc_handle;
+extern void mdelay(uint32_t msec);
+
+const unsigned char imx_power_domain_tree_desc[] =
+{
+ /* number of root nodes */
+ PWR_DOMAIN_AT_MAX_LVL,
+ /* number of child at the first node */
+ PLATFORM_CLUSTER_COUNT,
+ PLATFORM_CLUSTER0_CORE_COUNT,
+ PLATFORM_CLUSTER1_CORE_COUNT,
+};
+
+const static int ap_core_index[PLATFORM_CORE_COUNT] = {
+ SC_R_A53_0, SC_R_A53_1, SC_R_A53_2,
+ SC_R_A53_3, SC_R_A72_0, SC_R_A72_1,
+};
+
+plat_local_state_t plat_get_target_pwr_state(unsigned int lvl,
+ const plat_local_state_t *target_state,
+ unsigned int ncpu)
+{
+ /* TODO */
+ return 0;
+}
+
+void imx8qm_kill_cpu(unsigned int target_idx)
+{
+ tf_printf("kill cluster %d, cpu %d\n", target_idx / 4, target_idx % 4);
+
+ if (sc_pm_cpu_start(ipc_handle, ap_core_index[target_idx],
+ false, 0x80000000) != SC_ERR_NONE) {
+ ERROR("cluster %d core %d power down failed!\n",
+ target_idx / 4, target_idx % 4);
+ return;
+ }
+
+ if (sc_pm_set_resource_power_mode(ipc_handle, ap_core_index[target_idx],
+ SC_PM_PW_MODE_OFF) != SC_ERR_NONE) {
+ ERROR("cluster %d core %d power down failed!\n",
+ target_idx / 4, target_idx % 4);
+ return;
+ }
+}
+
+int imx_pwr_domain_on(u_register_t mpidr)
+{
+ int ret = PSCI_E_SUCCESS;
+ unsigned int cluster_id, cpu_id;
+
+ cluster_id = MPIDR_AFFLVL1_VAL(mpidr);
+ cpu_id = MPIDR_AFFLVL0_VAL(mpidr);
+
+ tf_printf("imx_pwr_domain_on cluster_id %d, cpu_id %d\n", cluster_id, cpu_id);
+
+ if (cluster_id == 0) {
+ if (sc_pm_set_resource_power_mode(ipc_handle, ap_core_index[cpu_id],
+ SC_PM_PW_MODE_ON) != SC_ERR_NONE) {
+ ERROR("cluster0 core %d power on failed!\n", cpu_id);
+ ret = PSCI_E_INTERN_FAIL;
+ }
+
+ if (sc_pm_cpu_start(ipc_handle, ap_core_index[cpu_id],
+ true, 0x80000000) != SC_ERR_NONE) {
+ ERROR("boot cluster0 core %d failed!\n", cpu_id);
+ ret = PSCI_E_INTERN_FAIL;
+ }
+ } else {
+ if (sc_pm_set_resource_power_mode(ipc_handle, SC_R_A72,
+ SC_PM_PW_MODE_ON) != SC_ERR_NONE) {
+ ERROR(" cluster1 power on failed!\n");
+ ret = PSCI_E_INTERN_FAIL;
+ }
+
+ if (sc_pm_set_resource_power_mode(ipc_handle, ap_core_index[cpu_id + 4],
+ SC_PM_PW_MODE_ON) != SC_ERR_NONE) {
+ ERROR(" cluster1 core %d power on failed!\n", cpu_id);
+ ret = PSCI_E_INTERN_FAIL;
+ }
+
+ if (sc_pm_cpu_start(ipc_handle, ap_core_index[cpu_id + 4],
+ true, 0x80000000) != SC_ERR_NONE) {
+ ERROR("boot cluster1 core %d failed!\n", cpu_id);
+ ret = PSCI_E_INTERN_FAIL;
+ }
+ }
+
+ return ret;
+}
+
+void imx_pwr_domain_on_finish(const psci_power_state_t *target_state)
+{
+ uint64_t mpidr = read_mpidr_el1();
+ unsigned int cluster_id = MPIDR_AFFLVL1_VAL(mpidr);
+ unsigned int cpu_id = MPIDR_AFFLVL0_VAL(mpidr);
+
+ if (cluster_id == 1 && cpu_id == 0)
+ cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(mpidr));
+
+ /* program the GIC per cpu dist and rdist interface */
+ plat_gic_pcpu_init();
+
+ /* enable the GICv3 cpu interface */
+ plat_gic_cpuif_enable();
+
+ tf_printf("cluster:%d core:%d is on\n", cluster_id, cpu_id);
+}
+
+void imx_pwr_domain_off(const psci_power_state_t *target_state)
+{
+ u_register_t mpidr = read_mpidr_el1();
+ unsigned int cluster_id = MPIDR_AFFLVL1_VAL(mpidr);
+ unsigned int cpu_id = MPIDR_AFFLVL0_VAL(mpidr);
+
+ plat_gic_cpuif_disable();
+ tf_printf("turn off cluster:%d core:%d\n", cluster_id, cpu_id);
+}
+
+int imx_validate_ns_entrypoint(uintptr_t ns_entrypoint)
+{
+ /* TODO */
+ return PSCI_E_SUCCESS;
+}
+
+int imx_validate_power_state(unsigned int power_state,
+ psci_power_state_t *req_state)
+{
+ int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
+
+ 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) {
+ 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;
+ }
+
+ return 0;
+}
+
+void imx_cpu_standby(plat_local_state_t cpu_state)
+{
+ dsb();
+
+ write_icc_igrpen1_el1(1);
+
+ write_scr_el3(read_scr_el3() | 0x4);
+ isb();
+
+ wfi();
+
+ write_icc_igrpen1_el1(0);
+ write_scr_el3(read_scr_el3() & (~0x4));
+ isb();
+}
+
+void imx_domain_suspend(const psci_power_state_t *target_state)
+{
+ /* TODO */
+}
+
+void imx_domain_suspend_finish(const psci_power_state_t *target_state)
+{
+ /* TODO */
+}
+
+void imx_get_sys_suspend_power_state(psci_power_state_t *req_state)
+{
+ unsigned int i;
+
+ for (i = IMX_PWR_LVL0; i <= PLAT_MAX_PWR_LVL; i++)
+ req_state->pwr_domain_state[i] = PLAT_MAX_RET_STATE;
+}
+
+static const plat_psci_ops_t imx_plat_psci_ops = {
+ .pwr_domain_on = imx_pwr_domain_on,
+ .pwr_domain_on_finish = imx_pwr_domain_on_finish,
+ .pwr_domain_off = imx_pwr_domain_off,
+ .validate_ns_entrypoint = imx_validate_ns_entrypoint,
+ .validate_power_state = imx_validate_power_state,
+ .cpu_standby = imx_cpu_standby,
+ .pwr_domain_suspend = imx_domain_suspend,
+ .pwr_domain_suspend_finish = imx_domain_suspend_finish,
+ .get_sys_suspend_power_state = imx_get_sys_suspend_power_state,
+};
+
+/* export the platform specific psci ops */
+int plat_setup_psci_ops(uintptr_t sec_entrypoint,
+ const plat_psci_ops_t **psci_ops)
+{
+ imx_mailbox_init(sec_entrypoint);
+ /* sec_entrypoint is used for warm reset */
+ *psci_ops = &imx_plat_psci_ops;
+
+ return 0;
+}