msm: platsmp-8625: Support core1 boot after power collapse.
On 8625 target the cores enter the power collapse only when both the
cores are in GDFS state. Once out of power collpase core0 needs to
bring core1 out of GDFS and that is achieved by raising an SPI from
core0. Writing to the MP_CORE_IPC register generates the interrupt on
ACSR_MP_CORE_IPC1. To enable the core1 to receive an SPI we need to
configure the gic after we come out of power collapse, which is
identified based on the flag which gets updated once we
enter power collapse.
Once core1 is in reset, we need to clear the ACSR_MP_CORE_IPC1
and also clear the pending SPI.
Change-Id: I0e3bd5ad516289e2a8490205cf2eb39df07eca99
Signed-off-by: Taniya Das <tdas@codeaurora.org>
diff --git a/arch/arm/mach-msm/platsmp-8625.c b/arch/arm/mach-msm/platsmp-8625.c
index 3c46d0f..82aeb16 100644
--- a/arch/arm/mach-msm/platsmp-8625.c
+++ b/arch/arm/mach-msm/platsmp-8625.c
@@ -17,6 +17,7 @@
#include <linux/jiffies.h>
#include <linux/smp.h>
#include <linux/io.h>
+#include <linux/interrupt.h>
#include <asm/cacheflush.h>
#include <asm/hardware/gic.h>
@@ -59,6 +60,78 @@
}
static DEFINE_SPINLOCK(boot_lock);
+static DEFINE_RAW_SPINLOCK(irq_controller_lock);
+
+/*
+ * MP_CORE_IPC will be used to generate interrupt and can be used by either
+ * of core.
+ * To bring core1 out of GDFS we need to raise the SPI using the MP_CORE_IPC.
+ */
+static void raise_clear_spi(unsigned int cpu, bool set)
+{
+ int value;
+
+ value = __raw_readl(MSM_CSR_BASE + 0x54);
+ if (set)
+ __raw_writel(value | BIT(cpu), MSM_CSR_BASE + 0x54);
+ else
+ __raw_writel(value & ~BIT(cpu), MSM_CSR_BASE + 0x54);
+ mb();
+}
+
+/*
+ * Configure the GIC after we come out of power collapse.
+ * This function will configure some of the GIC registers so as to prepare the
+ * core1 to receive an SPI(ACSR_MP_CORE_IPC1, (32 + 8)), which will bring
+ * core1 out of GDFS.
+ */
+static void core1_gic_configure_and_raise(void)
+{
+ unsigned int value = 0;
+
+ raw_spin_lock(&irq_controller_lock);
+
+ value = __raw_readl(MSM_QGIC_DIST_BASE + GIC_DIST_ACTIVE_BIT + 0x4);
+ value |= BIT(8);
+ __raw_writel(value, MSM_QGIC_DIST_BASE + GIC_DIST_ACTIVE_BIT + 0x4);
+ mb();
+
+ value = __raw_readl(MSM_QGIC_DIST_BASE + GIC_DIST_TARGET + 0x24);
+ value |= BIT(13);
+ __raw_writel(value, MSM_QGIC_DIST_BASE + GIC_DIST_TARGET + 0x24);
+ mb();
+
+ value = __raw_readl(MSM_QGIC_DIST_BASE + GIC_DIST_TARGET + 0x28);
+ value |= BIT(1);
+ __raw_writel(value, MSM_QGIC_DIST_BASE + GIC_DIST_TARGET + 0x28);
+ mb();
+
+ value = __raw_readl(MSM_QGIC_DIST_BASE + GIC_DIST_ENABLE_SET + 0x4);
+ value |= BIT(8);
+ __raw_writel(value, MSM_QGIC_DIST_BASE + GIC_DIST_ENABLE_SET + 0x4);
+ mb();
+
+ value = __raw_readl(MSM_QGIC_DIST_BASE + GIC_DIST_PENDING_SET + 0x4);
+ value |= BIT(8);
+ __raw_writel(value, MSM_QGIC_DIST_BASE + GIC_DIST_PENDING_SET + 0x4);
+ mb();
+
+ raise_clear_spi(1, true);
+ raw_spin_unlock(&irq_controller_lock);
+}
+
+void clear_pending_spi(unsigned int irq)
+{
+ struct irq_data *d = irq_get_irq_data(irq);
+ struct irq_chip *c = irq_data_get_irq_chip(d);
+
+ /* Clear the IRQ from the ENABLE_SET */
+ c->irq_mask(d);
+ local_irq_disable();
+ gic_clear_spi_pending(irq);
+ c->irq_unmask(d);
+ local_irq_enable();
+}
void __cpuinit platform_secondary_init(unsigned int cpu)
{
@@ -153,8 +226,16 @@
* Send the secondary CPU a soft interrupt, thereby causing
* the boot monitor to read the system wide flags register,
* and branch to the address found there.
+ *
+ * power_collapsed is the flag which will be updated for Powercollapse.
+ * Once we are out of PC, as Core1 will be in the state of GDFS which
+ * needs to be brought out by raising an SPI.
*/
- gic_raise_softirq(cpumask_of(cpu), 1);
+
+ if (power_collapsed)
+ core1_gic_configure_and_raise();
+ else
+ gic_raise_softirq(cpumask_of(cpu), 1);
timeout = jiffies + (1 * HZ);
while (time_before(jiffies, timeout)) {
@@ -165,6 +246,13 @@
udelay(10);
}
+ /* Now we should clear the pending SPI */
+ if (power_collapsed) {
+ raise_clear_spi(1, false);
+ clear_pending_spi(MSM8625_INT_ACSR_MP_CORE_IPC1);
+ power_collapsed = 0;
+ }
+
/*
* now the secondary core is starting up let it run its
* calibrations, then wait for it to finish
diff --git a/arch/arm/mach-msm/pm.h b/arch/arm/mach-msm/pm.h
index 892472b..4e9cc3c 100644
--- a/arch/arm/mach-msm/pm.h
+++ b/arch/arm/mach-msm/pm.h
@@ -27,6 +27,8 @@
#define msm_secondary_startup NULL
#endif
+extern int power_collapsed;
+
enum msm_pm_sleep_mode {
MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND,
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
diff --git a/arch/arm/mach-msm/pm2.c b/arch/arm/mach-msm/pm2.c
index 6b026ac..94bc98f 100644
--- a/arch/arm/mach-msm/pm2.c
+++ b/arch/arm/mach-msm/pm2.c
@@ -80,6 +80,7 @@
};
static int msm_pm_debug_mask;
+int power_collapsed;
module_param_named(
debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
);