Commit 0e91ff59 authored by Abel Vesa's avatar Abel Vesa

MLK-21399 plat: imx8mq: gpc: Workaround for ERR11171

This new workaround takes advantage of the per core IMR registers in GPC in
order to unmask the IRQ0, still generated by the 12bit in IOMUX_GPR register
(which now remains always set), so it can only wake up one core at the time.
Also, this entire workaround has now been moved here in TF-A, allowing the
kernel side to be minimal.

Another advantage this workaround brings is the removal of the 50us delay
(which was necessary before in gic_raise_softirq in kernel) by allowing the
core that is waking up to mask his own IRQ0 in the suspend finish callback.

One important change here is the way the cores are woken up in
dram_dvfs_handler. Since the wake up mechanism has changed from asserting the
12th bit in IOMUX_GPR and leaving the IMR1 1st bit on for each core to exactly
the reverse, that is, leaving the IOMUX_GPR 12th bit always set and then
masking/unmasking the IMR1 1st bit for each independent core, we need to use
the imx_gpc_core_wake to wake up the cores.

Also, the 50us udelay is moved to TF-A (inside imx_pwr_domain_off) from kernel
(gic_raise_softirq), since the new cpuidle workaround does not need it in order
to clean the IOMUX_GPC 12bit. For now, the udelay seems to be still needed
in order to delay the affinity info OFF for the dying core. This is something
that needs further investigation.
Signed-off-by: default avatarAbel Vesa <>
Reviewed-by: default avatarLeonard Crestez <>
parent 81e9d726
......@@ -249,16 +249,17 @@ int dram_dvfs_handler(uint32_t smc_fid,
/* make sure all the core in WFE */
online_cores &= ~(0x1 << (cpu_id * 8));
while (1) {
#if defined(PLAT_IMX8M)
mmio_write_32(0x30340004, mmio_read_32(0x30340004) | (1 << 12));
for (int i = 0; i < 4; i++) {
if (i != cpu_id && online_cores & (1 << (i * 8))) {
imx_gpc_core_wake(1 << i);
while (1) {
if (online_cores == wfe_done)
#if defined(PLAT_IMX8M)
mmio_write_32(0x30340004, mmio_read_32(0x30340004) & ~(1 << 12));
/* flush the L1/L2 cache */
......@@ -13,6 +13,7 @@
#define IMX_SIP_CPUFREQ 0xC2000001
#define IMX_SIP_SET_CPUFREQ 0x00
......@@ -546,6 +546,29 @@ static void imx_gpc_set_wake(uint32_t hwirq, unsigned int on)
gpc_wake_irqs[idx] & ~mask;
static void imx_gpc_mask_irq0(uint32_t core_id, uint32_t mask)
if (mask)
mmio_setbits_32(gpc_imr_offset[core_id], 1);
mmio_clrbits_32(gpc_imr_offset[core_id], 1);
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;
......@@ -642,6 +665,9 @@ void imx_gpc_init(void)
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);
val |= 0x40000000;
......@@ -697,6 +723,9 @@ int imx_gpc_handler(uint32_t smc_fid,
u_register_t x3)
switch(x1) {
imx_gpc_set_wake(x2, x3);
......@@ -8,6 +8,7 @@
#include <arch.h>
#include <arch_helpers.h>
#include <debug.h>
#include <drivers/delay_timer.h>
#include <stdbool.h>
#include <plat_imx8.h>
#include <psci.h>
......@@ -52,6 +53,9 @@ void imx_pwr_domain_off(const psci_power_state_t *target_state)
/* config the core for power down */
/* TODO: Find out why this is still
* needed in order not to break suspend */
int imx_validate_ns_entrypoint(uintptr_t ns_entrypoint)
......@@ -148,6 +152,8 @@ void imx_domain_suspend_finish(const psci_power_state_t *target_state)
/* check the core level power status */
if (is_local_state_off(CORE_PWR_STATE(target_state))) {
/* mark this core as awake by masking IRQ0 */
/* clear the core lpm setting */
imx_set_cpu_lpm(core_id, false);
/* enable the gic cpu interface */
......@@ -8,6 +8,10 @@
#define __IMX_SOC_H
void imx_gpc_set_m_core_pgc(unsigned int cpu, bool pdn);
#if defined(PLAT_IMX8M)
void imx_gpc_set_a53_core_awake(uint32_t core_id);
void imx_gpc_core_wake(uint32_t cpumask);
void imx_anamix_pre_suspend(void);
void imx_anamix_post_resume(void);
void imx_gpc_init(void);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment