summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-omap2/common.h2
-rw-r--r--arch/arm/mach-omap2/omap-smp.c13
-rw-r--r--arch/arm/mach-omap2/omap4-common.c34
3 files changed, 48 insertions, 1 deletions
diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h
index 70993a9fcb50..d29dbaa29621 100644
--- a/arch/arm/mach-omap2/common.h
+++ b/arch/arm/mach-omap2/common.h
@@ -276,6 +276,8 @@ static inline void __iomem *omap4_get_scu_base(void)
extern void __init gic_init_irq(void);
extern void gic_dist_disable(void);
+extern bool gic_dist_disabled(void);
+extern void gic_timer_retrigger(void);
extern void omap_smc1(u32 fn, u32 arg);
extern void __iomem *omap4_get_sar_ram_base(void);
extern void omap_do_wfi(void);
diff --git a/arch/arm/mach-omap2/omap-smp.c b/arch/arm/mach-omap2/omap-smp.c
index 7d9c0e3fedc4..49a08dfe8d88 100644
--- a/arch/arm/mach-omap2/omap-smp.c
+++ b/arch/arm/mach-omap2/omap-smp.c
@@ -134,11 +134,22 @@ static int __cpuinit omap4_boot_secondary(unsigned int cpu, struct task_struct *
* 2) CPU1 must re-enable the GIC distributor on
* it's wakeup path.
*/
- if (IS_PM44XX_ERRATUM(PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD))
+ if (IS_PM44XX_ERRATUM(PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD)) {
+ local_irq_disable();
gic_dist_disable();
+ }
clkdm_wakeup(cpu1_clkdm);
clkdm_allow_idle(cpu1_clkdm);
+
+ if (IS_PM44XX_ERRATUM(PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD)) {
+ while (gic_dist_disabled()) {
+ udelay(1);
+ cpu_relax();
+ }
+ gic_timer_retrigger();
+ local_irq_enable();
+ }
} else {
dsb_sev();
booted = true;
diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c
index 72cf396a0fc2..6f94b4e7b18d 100644
--- a/arch/arm/mach-omap2/omap4-common.c
+++ b/arch/arm/mach-omap2/omap4-common.c
@@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/memblock.h>
#include <linux/of_irq.h>
@@ -24,6 +25,7 @@
#include <asm/hardware/cache-l2x0.h>
#include <asm/mach/map.h>
#include <asm/memblock.h>
+#include <asm/smp_twd.h>
#include <plat/sram.h>
#include <plat/omap-secure.h>
@@ -42,6 +44,9 @@ static void __iomem *l2cache_base;
static void __iomem *sar_ram_base;
static void __iomem *gic_dist_base_addr;
+static void __iomem *twd_base;
+
+#define IRQ_LOCALTIMER 29
#ifdef CONFIG_OMAP4_ERRATA_I688
/* Used to implement memory barrier on DRAM path */
@@ -101,6 +106,9 @@ void __init gic_init_irq(void)
gic_dist_base_addr = ioremap(OMAP44XX_GIC_DIST_BASE, SZ_4K);
BUG_ON(!gic_dist_base_addr);
+ twd_base = ioremap(OMAP44XX_LOCAL_TWD_BASE, SZ_4K);
+ BUG_ON(!twd_base);
+
/* Static mapping, never released */
omap_irq_base = ioremap(OMAP44XX_GIC_CPU_BASE, SZ_512);
BUG_ON(!omap_irq_base);
@@ -116,6 +124,32 @@ void gic_dist_disable(void)
__raw_writel(0x0, gic_dist_base_addr + GIC_DIST_CTRL);
}
+bool gic_dist_disabled(void)
+{
+ return !(__raw_readl(gic_dist_base_addr + GIC_DIST_CTRL) & 0x1);
+}
+
+void gic_timer_retrigger(void)
+{
+ u32 twd_int = __raw_readl(twd_base + TWD_TIMER_INTSTAT);
+ u32 gic_int = __raw_readl(gic_dist_base_addr + GIC_DIST_PENDING_SET);
+ u32 twd_ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL);
+
+ if (twd_int && !(gic_int & BIT(IRQ_LOCALTIMER))) {
+ /*
+ * The local timer interrupt got lost while the distributor was
+ * disabled. Ack the pending interrupt, and retrigger it.
+ */
+ pr_warn("%s: lost localtimer interrupt\n", __func__);
+ __raw_writel(1, twd_base + TWD_TIMER_INTSTAT);
+ if (!(twd_ctrl & TWD_TIMER_CONTROL_PERIODIC)) {
+ __raw_writel(1, twd_base + TWD_TIMER_COUNTER);
+ twd_ctrl |= TWD_TIMER_CONTROL_ENABLE;
+ __raw_writel(twd_ctrl, twd_base + TWD_TIMER_CONTROL);
+ }
+ }
+}
+
#ifdef CONFIG_CACHE_L2X0
void __iomem *omap4_get_l2cache_base(void)