plat: imx8mm: Add support for imx8mm lpa

For i.MX8MM low power audio playback, when Linux suspend,
M4 still needs to be active for audio playback, so system
can NOT enter DSM mode but only force A core platform into
STOP mode, PLLs/NoC/DRAM need to be active as well and MU
interrupt wakeup needs to be enabled for waking up Linux
by MU message sent by M4.

Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
Signed-off-by: Bai Ping <ping.bai@nxp.com>
(cherry picked from commit 7b60954d68e074d6d0d1a6f828f4392cf7c7137d)
diff --git a/plat/imx/imx8mm/gpc.c b/plat/imx/imx8mm/gpc.c
index 19d69ab..d1697dc 100644
--- a/plat/imx/imx8mm/gpc.c
+++ b/plat/imx/imx8mm/gpc.c
@@ -130,6 +130,9 @@
 #define VPU_G2_PGC		0xf00
 #define VPU_H1_PGC		0xf40
 
+#define M4RCR 	0xC
+#define SRC_SCR_M4C_NON_SCLR_RST_MASK		(1 << 0)
+
 #define IMX_PD_DOMAIN(name)				\
 	{						\
 		.pwr_req = name##_PWR_REQ,		\
@@ -208,6 +211,21 @@
 
 static unsigned int pu_domain_status;
 
+bool imx_is_m4_enabled(void)
+{
+	return !( mmio_read_32(IMX_SRC_BASE + M4RCR)
+		& SRC_SCR_M4C_NON_SCLR_RST_MASK);
+}
+
+#define M4_LPA_ACTIVE	0x5555
+#define M4_LPA_IDLE	0x0
+#define SRC_GPR9	0x94
+
+bool imx_m4_lpa_active(void)
+{
+	return mmio_read_32(IMX_SRC_BASE + SRC_GPR9) & M4_LPA_ACTIVE;
+}
+
 void imx_set_cpu_secure_entry(int core_id, uintptr_t sec_entrypoint)
 {
 	uint64_t temp_base;
@@ -461,6 +479,20 @@
 			SLPCR_BYPASS_PMIC_READY | SLPCR_RBC_EN);
 
 	mmio_write_32(IMX_GPC_BASE + SLPCR, val);
+
+	if (imx_is_m4_enabled() && imx_m4_lpa_active()) {
+		val = mmio_read_32(IMX_GPC_BASE + SLPCR);
+		val |= SLPCR_A53_FASTWUP_STOP;
+		mmio_write_32(IMX_GPC_BASE + 0x14, val);
+			return;
+	}
+
+	if (!imx_is_m4_enabled()) {
+		/* mask M4 DSM trigger if M4 is NOT enabled */
+		val = mmio_read_32(IMX_GPC_BASE + LPCR_M4);
+		val |= 1 << 31;
+		mmio_write_32(IMX_GPC_BASE + LPCR_M4, val);
+	}
 }
 
 void imx_set_rbc_count(void)
@@ -489,6 +521,8 @@
 {
 	int i;
 	uint32_t pll_ctrl;
+	if (imx_is_m4_enabled() && imx_m4_lpa_active())
+		return;
 	/* bypass all the plls before enter DSM mode */
 	for (i = 0; i < ARRAY_SIZE(pll_ctrl_offset); i++) {
 		pll_ctrl = mmio_read_32(IMX_ANAMIX_BASE + pll_ctrl_offset[i]);
@@ -513,6 +547,8 @@
 {
 	int i;
 	uint32_t pll_ctrl;
+	if (imx_is_m4_enabled() && imx_m4_lpa_active())
+		return;
 	/* unbypass all the plls after exit from DSM mode */
 	for (i = 0; i < ARRAY_SIZE(pll_ctrl_offset); i++) {
 		pll_ctrl = mmio_read_32(IMX_ANAMIX_BASE + pll_ctrl_offset[i]);
@@ -554,20 +590,22 @@
 {
 	uint32_t val;
 
-	/* enable MASTER1 & MASTER2 power down in A53 LPM mode */
-	val = mmio_read_32(IMX_GPC_BASE + LPCR_A53_BSC);
-	val &= ~(1 << 7);
-	val &= ~(1 << 8);
-	mmio_write_32(IMX_GPC_BASE + LPCR_A53_BSC, val);
+	if (!imx_is_m4_enabled() || !imx_m4_lpa_active()) {
+		/* enable MASTER1 & MASTER2 power down in A53 LPM mode */
+		val = mmio_read_32(IMX_GPC_BASE + LPCR_A53_BSC);
+		val &= ~(1 << 7);
+		val &= ~(1 << 8);
+		mmio_write_32(IMX_GPC_BASE + LPCR_A53_BSC, val);
 
-	val = mmio_read_32(IMX_GPC_BASE + MST_CPU_MAPPING);
-	val |= (0x3 << 1);
-	mmio_write_32(IMX_GPC_BASE + MST_CPU_MAPPING, val);
+		val = mmio_read_32(IMX_GPC_BASE + MST_CPU_MAPPING);
+		val |= (0x3 << 1);
+		mmio_write_32(IMX_GPC_BASE + MST_CPU_MAPPING, val);
 
-	/* noc can only be power down when all the pu domain is off */
-	if (!pu_domain_status)
-		/* enable noc power down */
-		imx_noc_slot_config(true);
+		/* noc can only be power down when all the pu domain is off */
+		if (!pu_domain_status)
+			/* enable noc power down */
+			imx_noc_slot_config(true);
+	}
 	/*
 	 * gic redistributor context save must be called when
 	 * the GIC CPU interface is disabled and before distributor save.
@@ -579,24 +617,25 @@
 {
 	uint32_t val;
 
-	/* disable MASTER1 & MASTER2 power down in A53 LPM mode */
-	val = mmio_read_32(IMX_GPC_BASE + LPCR_A53_BSC);
-	val |= (1 << 7);
-	val |= (1 << 8);
-	mmio_write_32(IMX_GPC_BASE + LPCR_A53_BSC, val);
+	if (!imx_is_m4_enabled() || !imx_m4_lpa_active()) {
+		/* disable MASTER1 & MASTER2 power down in A53 LPM mode */
+		val = mmio_read_32(IMX_GPC_BASE + LPCR_A53_BSC);
+		val |= (1 << 7);
+		val |= (1 << 8);
+		mmio_write_32(IMX_GPC_BASE + LPCR_A53_BSC, val);
 
-	val = mmio_read_32(IMX_GPC_BASE + MST_CPU_MAPPING);
-	val &= ~(0x3 << 1);
-	mmio_write_32(IMX_GPC_BASE + MST_CPU_MAPPING, val);
+		val = mmio_read_32(IMX_GPC_BASE + MST_CPU_MAPPING);
+		val &= ~(0x3 << 1);
+		mmio_write_32(IMX_GPC_BASE + MST_CPU_MAPPING, val);
 
-	/* noc can only be power down when all the pu domain is off */
-	if (!pu_domain_status) {
-		/* re-init the tz380 if resume from noc power down */
-		imx8mm_tz380_init();
-		/* disable noc power down */
-		imx_noc_slot_config(false);
+		/* noc can only be power down when all the pu domain is off */
+		if (!pu_domain_status) {
+			/* re-init the tz380 if resume from noc power down */
+			imx8mm_tz380_init();
+			/* disable noc power down */
+			imx_noc_slot_config(false);
+		}
 	}
-
 	/* restore gic context */
 	plat_gic_restore(proc_num, &imx_gicv3_ctx);
 }
@@ -629,6 +668,13 @@
 		mmio_write_32(IMX_GPC_BASE + gpc_imr_offset[last_core] + i * 4,
 			      irq_mask);
 	}
+
+	/* enable the MU wakeup */
+	if (imx_is_m4_enabled()) {
+		val = mmio_read_32(IMX_GPC_BASE + GPC_IMR1_CORE0_A53 + 0x8);
+		val &= ~(1 << 24);
+		mmio_write_32(IMX_GPC_BASE + GPC_IMR1_CORE0_A53 + 0x8, val);
+	}
 }
 
 static void imx_gpc_set_wake_irq(uint32_t hwirq, uint32_t on)
@@ -863,11 +909,6 @@
 	val &= ~(0x3 << 1);
 	mmio_write_32(IMX_GPC_BASE + MST_CPU_MAPPING, val);
 
-	/* mask M4 DSM trigger if M4 is NOT enabled */
-	val = mmio_read_32(IMX_GPC_BASE + LPCR_M4);
-	val |= 1 << 31;
-	mmio_write_32(IMX_GPC_BASE + LPCR_M4, val);
-
 	/*set all mix/PU in A53 domain */
 	mmio_write_32(IMX_GPC_BASE + GPC_PGC_CPU_0_1_MAPPING, 0xffff);
 
diff --git a/plat/imx/imx8mm/imx8mm_bl31_setup.c b/plat/imx/imx8mm/imx8mm_bl31_setup.c
index 0729501..541939d 100644
--- a/plat/imx/imx8mm/imx8mm_bl31_setup.c
+++ b/plat/imx/imx8mm/imx8mm_bl31_setup.c
@@ -224,6 +224,11 @@
 #endif
 	bl31_tzc380_setup();
 
+	/* Assign M4 to domain 1 */
+	mmio_write_32(IMX_RDC_BASE + 0x204, 0x1);
+	mmio_write_32(IMX_RDC_BASE + 0x518, 0xfc);
+	mmio_write_32(IMX_RDC_BASE + 0x5A4, 0xf3);
+
 #if defined (CSU_RDC_TEST)
 	csu_test();
 	rdc_test();
diff --git a/plat/imx/imx8mm/imx8mm_psci.c b/plat/imx/imx8mm/imx8mm_psci.c
index 5b5a55a..e967476 100644
--- a/plat/imx/imx8mm/imx8mm_psci.c
+++ b/plat/imx/imx8mm/imx8mm_psci.c
@@ -122,7 +122,8 @@
 
 	/* do system level power mode setting */
 	if (is_local_state_retn(SYSTEM_PWR_STATE(target_state))) {
-		dram_enter_retention();
+		if (!imx_is_m4_enabled() || !imx_m4_lpa_active())
+			dram_enter_retention();
 		imx_set_sys_lpm(true);
 		imx_anamix_pre_suspend();
 		noc_wrapper_pre_suspend(core_id);
@@ -143,7 +144,8 @@
 		imx_set_sys_wakeup(core_id, false);
 		imx_anamix_post_resume();
 		imx_clear_rbc_count();
-		dram_exit_retention();
+		if (!imx_is_m4_enabled() || !imx_m4_lpa_active())
+			dram_exit_retention();
 		noc_wrapper_post_resume(core_id);
 	}
 
diff --git a/plat/imx/imx8mm/include/soc.h b/plat/imx/imx8mm/include/soc.h
index 475b0bc..00052d3 100644
--- a/plat/imx/imx8mm/include/soc.h
+++ b/plat/imx/imx8mm/include/soc.h
@@ -7,6 +7,8 @@
 #ifndef __IMX_SOC_H
 #define __IMX_SOC_H
 
+#include <stdbool.h>
+
 void imx_gpc_set_m_core_pgc(unsigned int cpu, bool pdn);
 void imx_anamix_pre_suspend(void);
 void imx_anamix_post_resume(void);
@@ -30,4 +32,7 @@
 void ddrc_enter_retention(void);
 void ddrc_exit_retention(void);
 
+bool imx_is_m4_enabled(void);
+bool imx_m4_lpa_active(void);
+
 #endif /* __IMX_SOC_H */