Skip to content

Commit

Permalink
ARM: OMAP4: retrigger localtimers after re-enabling gic
Browse files Browse the repository at this point in the history
'Workaround for ROM bug because of CA9 r2pX gic control'
register change disables the gic distributor while the secondary
cpu is being booted.  If a localtimer interrupt on the primary cpu
occurs when the distributor is turned off, the interrupt is lost,
and the localtimer never fires again.

Make the primary cpu wait for the secondary cpu to reenable the
gic distributor (with interrupts off for safety), and then
check if the pending bit is set in the localtimer but not the
gic.  If so, ack it in the localtimer, and reset the timer with
the minimum timeout to trigger a new timer interrupt.

Signed-off-by: Colin Cross <ccross@android.com>
[s-jan@ti.com: adapted to k3.4 + validated functionality]
Signed-off-by: Sebastien Jan <s-jan@ti.com>
[t-kristo@ti.com: dropped generic ARM kernel exports from the code, rebased
 to mainline]
Signed-off-by: Tero Kristo <t-kristo@ti.com>
Signed-off-by: Kevin Hilman <khilman@ti.com>
  • Loading branch information
Colin Cross authored and Kevin Hilman committed Nov 5, 2012
1 parent ff999b8 commit cd8ce15
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 1 deletion.
2 changes: 2 additions & 0 deletions arch/arm/mach-omap2/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
13 changes: 12 additions & 1 deletion arch/arm/mach-omap2/omap-smp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
34 changes: 34 additions & 0 deletions arch/arm/mach-omap2/omap4-common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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>
Expand All @@ -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 */
Expand Down Expand Up @@ -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);
Expand All @@ -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)
Expand Down

0 comments on commit cd8ce15

Please sign in to comment.