summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-imx/cpuidle-imx7d.c26
1 files changed, 26 insertions, 0 deletions
diff --git a/arch/arm/mach-imx/cpuidle-imx7d.c b/arch/arm/mach-imx/cpuidle-imx7d.c
index 37d12b14134c..d19d76ef75d7 100644
--- a/arch/arm/mach-imx/cpuidle-imx7d.c
+++ b/arch/arm/mach-imx/cpuidle-imx7d.c
@@ -84,6 +84,9 @@ static atomic_t master_wait = ATOMIC_INIT(0);
static void (*imx7d_wfi_in_iram_fn)(void __iomem *iram_vbase);
static struct imx7_cpuidle_pm_info *cpuidle_pm_info;
+/* Mapped for the kernel, unlike cpuidle_pm_info->gic_dist_base.vbase */
+static void __iomem *imx7d_cpuidle_gic_base;
+
static void imx_pen_lock(int cpu)
{
if (cpu == 0) {
@@ -122,6 +125,16 @@ static int imx7d_idle_finish(unsigned long val)
return 0;
}
+static bool imx7d_gic_sgis_pending(void)
+{
+ void __iomem *sgip_base = imx7d_cpuidle_gic_base + 0x1f20;
+
+ return (readl_relaxed(sgip_base + 0x0) |
+ readl_relaxed(sgip_base + 0x4) |
+ readl_relaxed(sgip_base + 0x8) |
+ readl_relaxed(sgip_base + 0xc));
+}
+
static int imx7d_enter_low_power_idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
{
@@ -141,6 +154,16 @@ static int imx7d_enter_low_power_idle(struct cpuidle_device *dev,
cpu_pm_enter();
if (atomic_inc_return(&master_lpi) == num_online_cpus() &&
cpuidle_pm_info->last_cpu == -1) {
+ /*
+ * GPC will not wake on SGIs so check for them
+ * manually here. At this point we know the other cpu
+ * is in wfi or waiting for the lock and can't send
+ * any additional IPIs.
+ */
+ if (imx7d_gic_sgis_pending()) {
+ index = -1;
+ goto skip_lpi_flow;
+ }
imx_gpcv2_set_lpm_mode(WAIT_UNCLOCKED);
imx_gpcv2_set_cpu_power_gate_in_idle(true);
cpu_cluster_pm_enter();
@@ -162,6 +185,7 @@ static int imx7d_enter_low_power_idle(struct cpuidle_device *dev,
if (cpuidle_pm_info->last_cpu == dev->cpu)
cpuidle_pm_info->last_cpu = -1;
+skip_lpi_flow:
cpu_pm_exit();
imx_pen_unlock(dev->cpu);
}
@@ -308,6 +332,8 @@ int __init imx7d_cpuidle_init(void)
cpuidle_pm_info->gic_dist_base.vbase =
(void __iomem *)IMX_IO_P2V(MX7D_GIC_BASE_ADDR);
+ imx7d_cpuidle_gic_base = ioremap(MX7D_GIC_BASE_ADDR, MX7D_GIC_SIZE);
+
imx7d_enable_rcosc();
/* code size should include cpuidle_pm_info size */