summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 339b07f)
raw | patch | inline | side by side (parent: 339b07f)
author | Dave Gerlach <d-gerlach@ti.com> | |
Fri, 2 Nov 2018 10:30:47 +0000 (16:00 +0530) | ||
committer | Tero Kristo <t-kristo@ti.com> | |
Tue, 6 Nov 2018 13:26:12 +0000 (15:26 +0200) |
The current implementation for preventing timer interrupts from
breaking suspend fully fixes the issue on am335x but the GIC present on
am437x cannot be directly acked using only the irqchip calls as is done
for am335x but requires an additional step.
Calling irqchip->irq_eoi only writes to the GIC_CPU_EOI register but for
an interrupt to be properly cleared by the GIC, a read from
GIC_CPU_INTACK must come first. The only place the irq-gic driver reads
this is in the actual interrupt handler so we cannot access it from the
driver.
To get around this, let's map the GIC_CPU_BASE and read the
GIC_CPU_INTACK register ourselves before calling irq_eoi to properly ack
late timer interrupts that show up during suspend on am437x.
Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
breaking suspend fully fixes the issue on am335x but the GIC present on
am437x cannot be directly acked using only the irqchip calls as is done
for am335x but requires an additional step.
Calling irqchip->irq_eoi only writes to the GIC_CPU_EOI register but for
an interrupt to be properly cleared by the GIC, a read from
GIC_CPU_INTACK must come first. The only place the irq-gic driver reads
this is in the actual interrupt handler so we cannot access it from the
driver.
To get around this, let's map the GIC_CPU_BASE and read the
GIC_CPU_INTACK register ourselves before calling irq_eoi to properly ack
late timer interrupts that show up during suspend on am437x.
Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
arch/arm/mach-omap2/timer.c | patch | blob | history |
index 37c0444564a342fcdeeaf13828724eedd60707bb..55295a9d1df8cd55d0f8cc75ab642f008ed48787 100644 (file)
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/irq.h>
+#include <linux/irqchip/arm-gic.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/slab.h>
#define INCREMENTER_DENUMERATOR_RELOAD_OFFSET 0x14
#define NUMERATOR_DENUMERATOR_MASK 0xfffff000
+#define AM43XX_GIC_CPU_BASE 0x48240100
+
+static void __iomem *gic_cpu_base;
+
/* Clockevent code */
static struct omap_dm_timer clkev;
return 0;
}
+static int omap_clkevt_late_ack_init(void)
+{
+ gic_cpu_base = ioremap(AM43XX_GIC_CPU_BASE, SZ_4K);
+
+ if (!gic_cpu_base)
+ return -ENOMEM;
+
+ return 0;
+}
+
static void omap_clkevt_late_ack(void)
{
+ u32 val;
+
if (!clkev_irq_chip)
return;
+ /*
+ * For the gic to properly clear an interrupt it must be read
+ * from INTACK register
+ */
+ if (gic_cpu_base)
+ val = readl_relaxed(gic_cpu_base + GIC_CPU_INTACK);
if (clkev_irq_chip->irq_ack)
clkev_irq_chip->irq_ack(&clkev_irq_desc->irq_data);
if (clkev_irq_chip->irq_eoi)
clkev_irq_desc = irq_to_desc(clkev.irq);
if (clkev_irq_desc)
clkev_irq_chip = irq_desc_get_chip(clkev_irq_desc);
+
}
+ if (soc_is_am437x())
+ omap_clkevt_late_ack_init();
+
pr_info("OMAP clockevent source: %s at %lu Hz\n", clockevent_gpt.name,
clkev.rate);
}