summaryrefslogtreecommitdiff
path: root/plat/freescale/imx8mq/gpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'plat/freescale/imx8mq/gpc.c')
-rw-r--r--plat/freescale/imx8mq/gpc.c456
1 files changed, 456 insertions, 0 deletions
diff --git a/plat/freescale/imx8mq/gpc.c b/plat/freescale/imx8mq/gpc.c
new file mode 100644
index 00000000..58744c24
--- /dev/null
+++ b/plat/freescale/imx8mq/gpc.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright 2017 NXP
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <debug.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <smcc_helpers.h>
+#include <std_svc.h>
+#include <types.h>
+#include <mmio.h>
+#include <platform_def.h>
+#include <fsl_sip.h>
+#include <soc.h>
+
+#define GPC_LPCR_A53_BSC 0x0
+#define GPC_LPCR_A53_BSC2 0x108
+#define GPC_LPCR_A53_AD 0x4
+#define GPC_LPCR_M4 0x8
+#define GPC_SLPCR 0x14
+#define GPC_MST_CPU_MAPPING 0x18
+#define GPC_PGC_ACK_SEL_A53 0x24
+#define GPC_IMR1_CORE0_A53 0x30
+#define GPC_IMR2_CORE0_A53 0x34
+#define GPC_IMR3_CORE0_A53 0x38
+#define GPC_IMR4_CORE0_A53 0x3c
+#define GPC_IMR1_CORE1_A53 0x40
+#define GPC_IMR2_CORE1_A53 0x44
+#define GPC_IMR3_CORE1_A53 0x48
+#define GPC_IMR4_CORE1_A53 0x4c
+#define GPC_IMR1_CORE2_A53 0x1c0
+#define GPC_IMR2_CORE2_A53 0x1c4
+#define GPC_IMR3_CORE2_A53 0x1c8
+#define GPC_IMR4_CORE2_A53 0x1cc
+#define GPC_IMR1_CORE3_A53 0x1d0
+#define GPC_IMR2_CORE3_A53 0x1d4
+#define GPC_IMR3_CORE3_A53 0x1d8
+#define GPC_IMR4_CORE3_A53 0x1dc
+#define GPC_PGC_CPU_0_1_MAPPING 0xec
+#define GPC_PGC_CORE0_TIMMING 0x804
+#define GPC_PGC_CORE1_TIMMING 0x844
+#define GPC_PGC_CORE2_TIMMING 0x884
+#define GPC_PGC_CORE3_TIMMING 0x8c4
+#define GPC_PGC_SCU_TIMMING 0x910
+#define GPC_SLOT0_CFG 0xb0
+
+#define LPCR_A53_BSC_CPU_CLK_ON_LPM 0x4000
+#define LPCR_A53_BSC_LPM0 0x3
+#define LPCR_A53_BSC_LPM1 0xc
+#define LPCR_A53_BSC2_LPM2 0x3
+#define LPCR_A53_BSC2_LPM3 0xc
+
+#define LPCR_A53_AD_L2PGE 0x80000000
+#define LPCR_A53_AD_EN_C3_PUP 0x8000000
+#define LPCR_A53_AD_EN_C3_IRQ_PUP 0x4000000
+#define LPCR_A53_AD_EN_C2_PUP 0x2000000
+#define LPCR_A53_AD_EN_C2_IRQ_PUP 0x1000000
+#define LPCR_A53_AD_EN_C3_PDN 0x80000
+#define LPCR_A53_AD_EN_C3_WFI_PDN 0x40000
+#define LPCR_A53_AD_EN_C2_PDN 0x20000
+#define LPCR_A53_AD_EN_C2_WFI_PDN 0x10000
+#define LPCR_A53_AD_EN_C1_PUP 0x800
+#define LPCR_A53_AD_EN_C1_IRQ_PUP 0x400
+#define LPCR_A53_AD_EN_C0_PUP 0x200
+#define LPCR_A53_AD_EN_C0_IRQ_PUP 0x100
+#define LPCR_A53_AD_EN_L2_WFI_PDN 0x20
+#define LPCR_A53_AD_EN_PLAT_PDN 0x10
+#define LPCR_A53_AD_EN_C1_PDN 0x8
+#define LPCR_A53_AD_EN_C1_WFI_PDN 0x4
+#define LPCR_A53_AD_EN_C0_PDN 0x2
+#define LPCR_A53_AD_EN_C0_WFI_PDN 0x1
+
+#define A53_LPM_WAIT 0x5
+#define A53_LPM_STOP 0xa
+
+#define SLPCR_EN_DSM 0x80000000
+#define SLPCR_RBC_EN 0x40000000
+#define SLPCR_VSTBY 0x4
+#define SLPCR_SBYOS 0x2
+#define SLPCR_BYPASS_PMIC_READY 0x1
+#define SLPCR_A53_FASTWUP_STOP (1 << 17)
+#define SLPCR_A53_FASTWUP_WAIT (1 << 16)
+
+#define GPC_CPU_PGC_SW_PUP_REQ 0xf0
+#define GPC_CPU_PGC_SW_PDN_REQ 0xf4
+#define BM_CPU_PGC_SW_PDN_PUP_REQ 0x1
+
+#define GPC_ARM_PGC 0x800
+#define PGC_PCR 0
+
+static uint32_t gpc_saved_imrs[128];
+static uint32_t gpc_wake_irqs[128];
+//static uint32_t gpc_mf_irqs[128];
+
+void imx_gpc_set_m_core_pgc(unsigned int cpu, bool pdn)
+{
+ uintptr_t val;
+
+ val = mmio_read_32(IMX_GPC_BASE + GPC_ARM_PGC + cpu * 64);
+ val &= ~(0x1 << PGC_PCR);
+
+ if(pdn)
+ val |= 0x1 << PGC_PCR;
+ mmio_write_32(IMX_GPC_BASE + GPC_ARM_PGC + cpu * 64, val);
+}
+
+void imx_gpc_set_lpm_mode(enum imx_cpu_pwr_mode mode)
+{
+ uint32_t val1, val2, val3, val4;
+
+ val1 = mmio_read_32(IMX_GPC_BASE + GPC_LPCR_A53_BSC);
+ val2 = mmio_read_32(IMX_GPC_BASE + GPC_LPCR_A53_BSC2);
+ val3 = mmio_read_32(IMX_GPC_BASE + GPC_SLPCR);
+ val4 = mmio_read_32(IMX_GPC_BASE + GPC_LPCR_A53_AD);
+
+ /* all core's LPM setting must be same */
+ val1 &= ~(LPCR_A53_BSC_LPM0 | LPCR_A53_BSC_LPM1);
+ val2 &= ~(LPCR_A53_BSC2_LPM2 | LPCR_A53_BSC2_LPM3);
+ val3 &= ~(SLPCR_EN_DSM | SLPCR_RBC_EN | SLPCR_VSTBY |
+ SLPCR_SBYOS | SLPCR_BYPASS_PMIC_READY);
+ val4 |= (1 << 5);
+
+ switch(mode) {
+ case WAIT_CLOCKED:
+ break;
+ case WAIT_UNCLOCKED:
+ val1 |= A53_LPM_WAIT;
+ val2 |= A53_LPM_WAIT;
+ val1 &= ~LPCR_A53_BSC_CPU_CLK_ON_LPM;
+ break;
+ case STOP_POWER_ON:
+ val1 |= A53_LPM_STOP;
+ val2 |= A53_LPM_STOP;
+ val1 &= ~LPCR_A53_BSC_CPU_CLK_ON_LPM;
+ val3 |= SLPCR_EN_DSM;
+ val3 |= SLPCR_RBC_EN;
+ val3 |= SLPCR_BYPASS_PMIC_READY;
+ break;
+ case STOP_POWER_OFF:
+ val1 |= A53_LPM_STOP;
+ val2 |= A53_LPM_STOP;
+ val1 &= ~LPCR_A53_BSC_CPU_CLK_ON_LPM;
+ val3 |= SLPCR_EN_DSM;
+ val3 |= SLPCR_A53_FASTWUP_STOP;
+ val3 |= SLPCR_RBC_EN;
+ val3 |= SLPCR_VSTBY;
+ val3 |= SLPCR_SBYOS;
+ val3 |= SLPCR_BYPASS_PMIC_READY;
+ val4 &= ~(1 << 5);
+ break;
+ default:
+ break;
+ }
+
+ mmio_write_32(IMX_GPC_BASE + GPC_LPCR_A53_BSC, val1);
+ mmio_write_32(IMX_GPC_BASE + GPC_LPCR_A53_BSC2, val2);
+ mmio_write_32(IMX_GPC_BASE + GPC_LPCR_A53_AD, val4);
+ mmio_write_32(IMX_GPC_BASE + GPC_SLPCR, val3);
+}
+
+/* enable CORE LPM PDN/WUP in AD register */
+void imx_gpc_set_cpu_power_gate_by_lpm(unsigned int cpu, bool pdn)
+{
+ uint32_t mask, val;
+
+ val = mmio_read_32(IMX_GPC_BASE + GPC_LPCR_A53_AD);
+
+ switch(cpu) {
+ case 0:
+ mask = LPCR_A53_AD_EN_C0_PDN | LPCR_A53_AD_EN_C0_PUP;
+ break;
+ case 1:
+ mask = LPCR_A53_AD_EN_C1_PDN | LPCR_A53_AD_EN_C1_PUP;
+ break;
+ case 2:
+ mask = LPCR_A53_AD_EN_C2_PDN | LPCR_A53_AD_EN_C3_PUP;
+ break;
+ case 3:
+ mask = LPCR_A53_AD_EN_C3_PDN | LPCR_A53_AD_EN_C3_PUP;
+ break;
+ default:
+ return;
+ }
+
+ if (pdn)
+ val |= mask;
+ else
+ val &= ~mask;
+
+ mmio_write_32(IMX_GPC_BASE + GPC_LPCR_A53_AD, val);
+}
+
+/* enable PLAT/SCU power down in AD register */
+void imx_gpc_set_plat_power_gate_by_lpm(bool pdn)
+{
+ uint32_t val;
+
+ val = mmio_read_32(IMX_GPC_BASE + GPC_LPCR_A53_AD);
+ val &= ~(LPCR_A53_AD_EN_PLAT_PDN | LPCR_A53_AD_L2PGE);
+
+ if (pdn)
+ val |= LPCR_A53_AD_EN_PLAT_PDN | LPCR_A53_AD_L2PGE;
+
+ mmio_write_32(IMX_GPC_BASE + GPC_LPCR_A53_AD, val);
+}
+
+void imx_gpc_set_slot_ack(uint32_t index, enum imx_gpc_slot m_core,
+ bool mode, bool ack)
+{
+ uint32_t val, shift;
+
+ if (index > 10) {
+ tf_printf("Invalid slot index\n");
+ return;
+ }
+ /* set slot */
+ val = mmio_read_32(IMX_GPC_BASE + GPC_SLOT0_CFG + index * 4);
+ val |= (mode + 1) << m_core * 2;
+ mmio_write_32(IMX_GPC_BASE + GPC_SLOT0_CFG + index * 4, val);
+ /* set ack */
+ if (ack) {
+ shift = m_core >= A53_SCU ? 2 : 0;
+ val = mmio_read_32(IMX_GPC_BASE + GPC_PGC_ACK_SEL_A53);
+ /* clear dummy ack */
+ val &= ~(1 << (15 + (mode ? 16: 0)));
+ val |= 1 << (shift + (mode ? 16 : 0));
+ mmio_write_32(IMX_GPC_BASE + GPC_PGC_ACK_SEL_A53, val);
+ }
+}
+
+/* cpu: cpu index */
+void imx_gpc_set_core_pdn_pup_by_software(unsigned int cpu, bool pdn)
+{
+ uint32_t val, index;
+
+ val = mmio_read_32(IMX_GPC_BASE + (pdn ?
+ GPC_CPU_PGC_SW_PDN_REQ : GPC_CPU_PGC_SW_PUP_REQ));
+
+ /*Set the core PCR bit before sw PUP/PDN trigger */
+ imx_gpc_set_m_core_pgc(cpu, true);
+
+ index = cpu < 2 ? cpu : cpu + 1;
+ val |= (BM_CPU_PGC_SW_PDN_PUP_REQ << index);
+ mmio_write_32(IMX_GPC_BASE + (pdn ?
+ GPC_CPU_PGC_SW_PDN_REQ : GPC_CPU_PGC_SW_PUP_REQ), val);
+
+ while((mmio_read_32(IMX_GPC_BASE + (pdn ?
+ GPC_CPU_PGC_SW_PDN_REQ : GPC_CPU_PGC_SW_PUP_REQ)) &
+ BM_CPU_PGC_SW_PDN_PUP_REQ) != 0)
+ ;
+
+ /*Clear the core PCR bit after sw PUP/PDN trigger */
+ imx_gpc_set_m_core_pgc(cpu, false);
+}
+
+void imx_gpc_pre_suspend(bool arm_power_off)
+{
+ unsigned int i;
+
+ /* set the LPM mode */
+ if (arm_power_off) {
+ /* enable core 0/1/2/3 power down/up with low power mode */
+ /* enable plat power down/up with low power mode */
+
+ /*
+ * to avoid confuse, we use slot 0~4 for power down.
+ * slot 5~9 for power up.
+ * power down slot sequence:
+ * slot 0 -> CORE0,
+ * SLOT 1 -> Mega/Fast mix,
+ * SLOT 2 -> SCU
+ *
+ * SLOT 5 -> Mega/Fast mix,
+ * SLOT 6 -> SCU
+ * SLOT 7 -> CORE0
+ */
+ /* SCU slot ack as the power down ack */
+ /* CORE0 slot ack as the power up ack */
+ imx_gpc_set_lpm_mode(STOP_POWER_OFF);
+ imx_gpc_set_slot_ack(0, A53_CORE0, false, false);
+ imx_gpc_set_slot_ack(2, A53_SCU, false, true);
+
+ imx_gpc_set_slot_ack(6, A53_SCU, true, false);
+ imx_gpc_set_slot_ack(7, A53_CORE0, true, true);
+ imx_gpc_set_m_core_pgc(0, true);
+ imx_gpc_set_m_core_pgc(4, true);
+
+ imx_gpc_set_cpu_power_gate_by_lpm(0, true);
+ imx_gpc_set_plat_power_gate_by_lpm(true);
+ } else {
+ imx_gpc_set_lpm_mode(STOP_POWER_ON);
+ }
+
+ for (i = 0; i < 4; i++) {
+ gpc_saved_imrs[i] = mmio_read_32(IMX_GPC_BASE + GPC_IMR1_CORE0_A53 + i * 4);
+ mmio_write_32(IMX_GPC_BASE + GPC_IMR1_CORE0_A53 + i * 4, ~gpc_wake_irqs[i]);
+ }
+
+}
+
+void imx_gpc_post_resume(void)
+{
+ int i;
+ /* set LPM mode WAIT CLOCKED */
+ imx_gpc_set_lpm_mode(WAIT_CLOCKED);
+ /* clear lpm power gate of core and plat */
+ imx_gpc_set_cpu_power_gate_by_lpm(0, false);
+ imx_gpc_set_plat_power_gate_by_lpm(false);
+ /* clear PGC PDN bit */
+ imx_gpc_set_m_core_pgc(0, false);
+ imx_gpc_set_m_core_pgc(4, false);
+
+ for (i = 0; i < 4; i++) {
+ mmio_write_32(IMX_GPC_BASE + GPC_IMR1_CORE0_A53 + i * 4, gpc_saved_imrs[i]);
+ }
+
+ /* skip slot m4 use , clear slots */
+ for(i = 0; i < 10; i ++) {
+ if (i == 1 || i == 5)
+ continue;
+ mmio_write_32(IMX_GPC_BASE +GPC_SLOT0_CFG + i * 0x4, 0x0);
+ }
+ /* set DUMMY PDN/PUP ACK by default for A53 domain */
+ mmio_write_32(IMX_GPC_BASE + GPC_PGC_ACK_SEL_A53, 1 << 31 | 1 << 15);
+}
+
+static void imx_gpc_hwirq_mask(unsigned int hwirq)
+{
+ uintptr_t reg;
+ unsigned int val;
+
+ reg = IMX_GPC_BASE + GPC_IMR1_CORE0_A53 + (hwirq / 32) * 4;
+ val = mmio_read_32(reg);
+ val |= 1 << hwirq % 32;
+ mmio_write_32(reg, val);
+}
+
+static void imx_gpc_hwirq_unmask(unsigned int hwirq)
+{
+ uintptr_t reg;
+ unsigned int val;
+
+ reg = IMX_GPC_BASE + GPC_IMR1_CORE0_A53 + (hwirq / 32) * 4;
+ val = mmio_read_32(reg);
+ val &= ~(1 << hwirq % 32);
+ mmio_write_32(reg, val);
+}
+
+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_pm_domain_enable(uint32_t domain_id, uint32_t on)
+{
+ uint32_t val;
+ uintptr_t reg;
+
+ reg = IMX_GPC_BASE + (on ? 0xf8 : 0x104);
+ val = 1 << domain_id;
+ mmio_write_32(reg, val);
+ while(mmio_read_32(reg) & val)
+ ;
+}
+
+void imx_gpc_init(void)
+{
+ unsigned int val;
+ int i;
+ /* mask all the interrupt by default */
+ for (i = 0; i < 4; i ++) {
+ mmio_write_32(IMX_GPC_BASE + GPC_IMR1_CORE0_A53 + i * 4, ~0x0);
+ mmio_write_32(IMX_GPC_BASE + GPC_IMR1_CORE1_A53 + i * 4, ~0x0);
+ mmio_write_32(IMX_GPC_BASE + GPC_IMR1_CORE2_A53 + i * 4, ~0x0);
+ mmio_write_32(IMX_GPC_BASE + GPC_IMR1_CORE3_A53 + 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 + GPC_IMR1_CORE0_A53, ~0x1);
+
+ /* use external IRQs to wakeup C0~C3 from LPM */
+ val = mmio_read_32(IMX_GPC_BASE + GPC_LPCR_A53_BSC);
+ val |= 0x70c00000;
+ /* clear the MASTER0 LPM handshake */
+ val &= ~(1 << 6);
+ mmio_write_32(IMX_GPC_BASE + GPC_LPCR_A53_BSC, val);
+
+ /* mask M4 DSM trigger if M4 is NOT enabled */
+ val = mmio_read_32(IMX_GPC_BASE + GPC_LPCR_M4);
+ val |= 1 << 31;
+ mmio_write_32(IMX_GPC_BASE + GPC_LPCR_M4, val);
+
+ /* set mega/fast mix in A53 domain */
+ mmio_write_32(IMX_GPC_BASE + GPC_PGC_CPU_0_1_MAPPING, 0x1);
+
+ /* set SCU timming */
+ mmio_write_32(IMX_GPC_BASE + GPC_PGC_SCU_TIMMING,
+ (0x59 << 10) | 0x5B | (0x2 << 20));
+
+ /* set A53 core power up timming */
+ mmio_write_32(IMX_GPC_BASE + GPC_PGC_CORE0_TIMMING, 0x1a << 7);
+ mmio_write_32(IMX_GPC_BASE + GPC_PGC_CORE1_TIMMING, 0x1a << 7);
+ mmio_write_32(IMX_GPC_BASE + GPC_PGC_CORE2_TIMMING, 0x1a << 7);
+ mmio_write_32(IMX_GPC_BASE + GPC_PGC_CORE3_TIMMING, 0x1a << 7);
+
+ /* set DUMMY PDN/PUP ACK by default for A53 domain */
+ mmio_write_32(IMX_GPC_BASE + GPC_PGC_ACK_SEL_A53, 1 << 31 | 1 << 15);
+ /* clear DSM by default */
+ val = mmio_read_32(IMX_GPC_BASE + GPC_SLPCR);
+ val &= ~(1 << 31);
+ /* TODO if M4 is not enabled, clear more SLPCR bits */
+ mmio_write_32(IMX_GPC_BASE + GPC_SLPCR, val);
+}
+
+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_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_PM_DOMAIN:
+ imx_gpc_pm_domain_enable(x2, x3);
+ break;
+ default:
+ return SMC_UNK;
+ }
+
+ return 0;
+}