feat(imx8ulp): add i.MX8ULP basic support
Add the basic support for i.MX8ULP.
The i.MX 8ULP family of processors features NXP’s advanced
implementation of the dual Arm Cortex-A35 cores alongside
an Arm Cortex-M33. This combined architecture enables the
device to run a rich operating system (such as Linux) on
the Cortex-A35 core and an RTOS (such as FreeRTOS) on the
Cortex-M33 core. It also includes a Cadence Tensilica Fusion
DSP for low-power audio and a HiFi4 DSP for advanced audio
and machine learning applications.
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Signed-off-by: Ye Li <ye.li@nxp.com>
Signed-off-by: Jacky Bai <ping.bai@nxp.com>
Change-Id: I12df622b95960bcdf7da52e4c66470a700690e36
diff --git a/plat/imx/imx8ulp/imx8ulp_psci.c b/plat/imx/imx8ulp/imx8ulp_psci.c
new file mode 100644
index 0000000..3f23f68
--- /dev/null
+++ b/plat/imx/imx8ulp/imx8ulp_psci.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2021-2024 NXP
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdbool.h>
+
+#include <arch.h>
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <drivers/arm/gicv3.h>
+#include <lib/mmio.h>
+#include <lib/psci/psci.h>
+
+#include <plat_imx8.h>
+
+static uintptr_t secure_entrypoint;
+
+#define CORE_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL0])
+#define CLUSTER_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL1])
+#define SYSTEM_PWR_STATE(state) ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL])
+
+#define RVBARADDRx(c) (IMX_SIM1_BASE + 0x5c + 0x4 * (c))
+#define WKPUx(c) (IMX_SIM1_BASE + 0x3c + 0x4 * (c))
+#define AD_COREx_LPMODE(c) (IMX_CMC1_BASE + 0x50 + 0x4 * (c))
+
+static int imx_pwr_set_cpu_entry(unsigned int cpu, unsigned int entry)
+{
+ mmio_write_32(RVBARADDRx(cpu), entry);
+
+ /* set update bit */
+ mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) | BIT_32(24 + cpu));
+ /* wait for ack */
+ while (!(mmio_read_32(IMX_SIM1_BASE + 0x8) & BIT_32(26 + cpu))) {
+ }
+
+ /* clear update bit */
+ mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) & ~BIT_32(24 + cpu));
+ /* clear ack bit */
+ mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) | BIT_32(26 + cpu));
+
+ return 0;
+}
+
+int imx_pwr_domain_on(u_register_t mpidr)
+{
+ unsigned int cpu = MPIDR_AFFLVL0_VAL(mpidr);
+
+ imx_pwr_set_cpu_entry(cpu, secure_entrypoint);
+
+ mmio_write_32(IMX_CMC1_BASE + 0x18, 0x3f);
+ mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0);
+
+ /* enable wku wakeup for idle */
+ mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0xffffffff);
+
+ return PSCI_E_SUCCESS;
+}
+
+void imx_pwr_domain_on_finish(const psci_power_state_t *target_state)
+{
+ imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY);
+ plat_gic_pcpu_init();
+ plat_gic_cpuif_enable();
+}
+
+int imx_validate_ns_entrypoint(uintptr_t ns_entrypoint)
+{
+ return PSCI_E_SUCCESS;
+}
+
+void imx_pwr_domain_off(const psci_power_state_t *target_state)
+{
+ unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1());
+
+ plat_gic_cpuif_disable();
+
+ /* disable wakeup */
+ mmio_write_32(WKPUx(cpu), 0);
+
+ mmio_write_32(AD_COREx_LPMODE(cpu), 0x3);
+}
+
+void __dead2 imx8ulp_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state)
+{
+ while (1) {
+ wfi();
+ }
+}
+
+void __dead2 imx8ulp_system_reset(void)
+{
+ imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY);
+
+ /* Write invalid command to WDOG CNT to trigger reset */
+ mmio_write_32(IMX_WDOG3_BASE + 0x4, 0x12345678);
+
+ while (true) {
+ wfi();
+ }
+}
+
+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,
+ .validate_ns_entrypoint = imx_validate_ns_entrypoint,
+ .system_reset = imx8ulp_system_reset,
+ .pwr_domain_off = imx_pwr_domain_off,
+ .pwr_domain_pwr_down_wfi = imx8ulp_pwr_domain_pwr_down_wfi,
+};
+
+int plat_setup_psci_ops(uintptr_t sec_entrypoint,
+ const plat_psci_ops_t **psci_ops)
+{
+ secure_entrypoint = sec_entrypoint;
+ imx_pwr_set_cpu_entry(0, sec_entrypoint);
+ *psci_ops = &imx_plat_psci_ops;
+
+ mmio_write_32(IMX_CMC1_BASE + 0x18, 0x3f);
+ mmio_write_32(IMX_SIM1_BASE + 0x3c, 0xffffffff);
+
+ return 0;
+}