summaryrefslogtreecommitdiff
path: root/plat/imx/imx8m/imx8mq/gpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'plat/imx/imx8m/imx8mq/gpc.c')
-rw-r--r--plat/imx/imx8m/imx8mq/gpc.c218
1 files changed, 206 insertions, 12 deletions
diff --git a/plat/imx/imx8m/imx8mq/gpc.c b/plat/imx/imx8m/imx8mq/gpc.c
index 9b6e9951..c15c8739 100644
--- a/plat/imx/imx8m/imx8mq/gpc.c
+++ b/plat/imx/imx8m/imx8mq/gpc.c
@@ -8,16 +8,183 @@
#include <stdint.h>
#include <stdbool.h>
+#include <arch_helpers.h>
#include <common/debug.h>
#include <common/runtime_svc.h>
#include <drivers/delay_timer.h>
#include <lib/mmio.h>
#include <lib/psci/psci.h>
+#include <lib/spinlock.h>
#include <platform_def.h>
+#include <plat/common/platform.h>
#include <services/std_svc.h>
#include <gpc.h>
+#define FSL_SIP_CONFIG_GPC_MASK 0x00
+#define FSL_SIP_CONFIG_GPC_UNMASK 0x01
+#define FSL_SIP_CONFIG_GPC_SET_WAKE 0x02
+#define FSL_SIP_CONFIG_GPC_PM_DOMAIN 0x03
+#define FSL_SIP_CONFIG_GPC_SET_AFF 0x04
+#define FSL_SIP_CONFIG_GPC_CORE_WAKE 0x05
+
+static uint32_t gpc_saved_imrs[16];
+static uint32_t gpc_wake_irqs[4];
+static uint32_t gpc_imr_offset[] = {
+ IMX_GPC_BASE + IMR1_CORE0_A53,
+ IMX_GPC_BASE + IMR1_CORE1_A53,
+ IMX_GPC_BASE + IMR1_CORE2_A53,
+ IMX_GPC_BASE + IMR1_CORE3_A53,
+ IMX_GPC_BASE + IMR1_CORE0_M4,
+};
+
+spinlock_t gpc_imr_lock[4];
+
+static void gpc_imr_core_spin_lock(unsigned int core_id)
+{
+ spin_lock(&gpc_imr_lock[core_id]);
+}
+
+static void gpc_imr_core_spin_unlock(unsigned int core_id)
+{
+ spin_unlock(&gpc_imr_lock[core_id]);
+}
+
+static void gpc_save_imr_lpm(unsigned int core_id, unsigned int imr_idx)
+{
+ uint32_t reg = gpc_imr_offset[core_id] + imr_idx * 4;
+
+ gpc_imr_core_spin_lock(core_id);
+
+ gpc_saved_imrs[core_id + imr_idx * 4] = mmio_read_32(reg);
+ mmio_write_32(reg, ~gpc_wake_irqs[imr_idx]);
+
+ gpc_imr_core_spin_unlock(core_id);
+}
+
+static void gpc_restore_imr_lpm(unsigned int core_id, unsigned int imr_idx)
+{
+ uint32_t reg = gpc_imr_offset[core_id] + imr_idx * 4;
+ uint32_t val = gpc_saved_imrs[core_id + imr_idx * 4];
+
+ gpc_imr_core_spin_lock(core_id);
+
+ mmio_write_32(reg, val);
+
+ gpc_imr_core_spin_unlock(core_id);
+}
+
+/*
+ * On i.MX8MQ, only in system suspend mode, the A53 cluster can
+ * enter LPM mode and shutdown the A53 PLAT power domain. So LPM
+ * wakeup only used for system suspend. when system enter suspend,
+ * any A53 CORE can be the last core to suspend the system, But
+ * the LPM wakeup can only use the C0's IMR to wakeup A53 cluster
+ * from LPM, so save C0's IMRs before suspend, restore back after
+ * resume.
+ */
+void imx_set_sys_wakeup(unsigned int last_core, bool pdn)
+{
+ unsigned int imr, core;
+
+ if (pdn)
+ for (imr = 0; imr < 4; imr++)
+ for (core = 0; core < 4; core++)
+ gpc_save_imr_lpm(core, imr);
+ else
+ for (imr = 0; imr < 4; imr++)
+ for (core = 0; core < 4; core++)
+ gpc_restore_imr_lpm(core, imr);
+}
+
+static void imx_gpc_hwirq_mask(unsigned int hwirq)
+{
+ uintptr_t reg;
+ unsigned int val;
+
+ gpc_imr_core_spin_lock(0);
+ reg = gpc_imr_offset[0] + (hwirq / 32) * 4;
+ val = mmio_read_32(reg);
+ val |= 1 << hwirq % 32;
+ mmio_write_32(reg, val);
+ gpc_imr_core_spin_unlock(0);
+}
+
+static void imx_gpc_hwirq_unmask(unsigned int hwirq)
+{
+ uintptr_t reg;
+ unsigned int val;
+
+ gpc_imr_core_spin_lock(0);
+ reg = gpc_imr_offset[0] + (hwirq / 32) * 4;
+ val = mmio_read_32(reg);
+ val &= ~(1 << hwirq % 32);
+ mmio_write_32(reg, val);
+ gpc_imr_core_spin_unlock(0);
+}
+
+static void imx_gpc_set_wake(uint32_t hwirq, unsigned int on)
+{
+ uint32_t mask, idx;
+
+ mask = 1 << hwirq % 32;
+ idx = hwirq / 32;
+ gpc_wake_irqs[idx] = on ? gpc_wake_irqs[idx] | mask :
+ gpc_wake_irqs[idx] & ~mask;
+}
+
+static void imx_gpc_mask_irq0(uint32_t core_id, uint32_t mask)
+{
+ gpc_imr_core_spin_lock(core_id);
+ if (mask)
+ mmio_setbits_32(gpc_imr_offset[core_id], 1);
+ else
+ mmio_clrbits_32(gpc_imr_offset[core_id], 1);
+ dsb();
+ gpc_imr_core_spin_unlock(core_id);
+}
+
+void imx_gpc_core_wake(uint32_t cpumask)
+{
+ for (int i = 0; i < 4; i++)
+ if (cpumask & (1 << i))
+ imx_gpc_mask_irq0(i, false);
+}
+
+void imx_gpc_set_a53_core_awake(uint32_t core_id)
+{
+ imx_gpc_mask_irq0(core_id, true);
+}
+
+static void imx_gpc_set_affinity(uint32_t hwirq, unsigned cpu_idx)
+{
+ uintptr_t reg;
+ unsigned int val;
+
+ /*
+ * using the mask/unmask bit as affinity function.unmask the
+ * IMR bit to enable IRQ wakeup for this core.
+ */
+ gpc_imr_core_spin_lock(cpu_idx);
+ reg = gpc_imr_offset[cpu_idx] + (hwirq / 32) * 4;
+ val = mmio_read_32(reg);
+ val &= ~(1 << hwirq % 32);
+ mmio_write_32(reg, val);
+ gpc_imr_core_spin_unlock(cpu_idx);
+
+ /* clear affinity of other core */
+ for (int i = 0; i < 4; i++) {
+ if (cpu_idx != i) {
+ gpc_imr_core_spin_lock(i);
+ reg = gpc_imr_offset[i] + (hwirq / 32) * 4;
+ val = mmio_read_32(reg);
+ val |= (1 << hwirq % 32);
+ mmio_write_32(reg, val);
+ gpc_imr_core_spin_unlock(i);
+ }
+ }
+}
+
/* use wfi power down the core */
void imx_set_cpu_pwr_off(unsigned int core_id)
{
@@ -125,26 +292,53 @@ void imx_set_cluster_powerdown(unsigned int last_core, uint8_t power_state)
}
}
+int imx_gpc_handler(uint32_t smc_fid,
+ u_register_t x1,
+ u_register_t x2,
+ u_register_t x3)
+{
+ switch(x1) {
+ case FSL_SIP_CONFIG_GPC_CORE_WAKE:
+ imx_gpc_core_wake(x2);
+ break;
+ case FSL_SIP_CONFIG_GPC_SET_WAKE:
+ imx_gpc_set_wake(x2, x3);
+ break;
+ case FSL_SIP_CONFIG_GPC_MASK:
+ imx_gpc_hwirq_mask(x2);
+ break;
+ case FSL_SIP_CONFIG_GPC_UNMASK:
+ imx_gpc_hwirq_unmask(x2);
+ break;
+ case FSL_SIP_CONFIG_GPC_SET_AFF:
+ imx_gpc_set_affinity(x2, x3);
+ break;
+ default:
+ return SMC_UNK;
+ }
+
+ return 0;
+}
+
void imx_gpc_init(void)
{
uint32_t val;
- int i;
+ int i, j;
+
/* mask all the interrupt by default */
- for (i = 0; i < 4; i++) {
- mmio_write_32(IMX_GPC_BASE + IMR1_CORE0_A53 + i * 4, ~0x0);
- mmio_write_32(IMX_GPC_BASE + IMR1_CORE1_A53 + i * 4, ~0x0);
- mmio_write_32(IMX_GPC_BASE + IMR1_CORE2_A53 + i * 4, ~0x0);
- mmio_write_32(IMX_GPC_BASE + IMR1_CORE3_A53 + i * 4, ~0x0);
- mmio_write_32(IMX_GPC_BASE + IMR1_CORE0_M4 + i * 4, ~0x0);
- }
+ for (i = 0; i < 4; i++)
+ for (j = 0; j < 5; j++)
+ mmio_write_32(gpc_imr_offset[j] + i * 4, ~0x0);
+
/* Due to the hardware design requirement, need to make
* sure GPR interrupt(#32) is unmasked during RUN mode to
* avoid entering DSM mode by mistake.
*/
- mmio_write_32(IMX_GPC_BASE + IMR1_CORE0_A53, 0xFFFFFFFE);
- mmio_write_32(IMX_GPC_BASE + IMR1_CORE1_A53, 0xFFFFFFFE);
- mmio_write_32(IMX_GPC_BASE + IMR1_CORE2_A53, 0xFFFFFFFE);
- mmio_write_32(IMX_GPC_BASE + IMR1_CORE3_A53, 0xFFFFFFFE);
+ for (i = 0; i < 4; i++)
+ mmio_write_32(gpc_imr_offset[i], ~0x1);
+
+ /* leave the IOMUX_GPC bit 12 on for core wakeup */
+ mmio_setbits_32(IMX_IOMUX_GPR_BASE + 0x4, 1 << 12);
/* use external IRQs to wakeup C0~C3 from LPM */
val = mmio_read_32(IMX_GPC_BASE + LPCR_A53_BSC);