aboutsummaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/irq.c')
-rw-r--r--arch/arm/mach-tegra/irq.c136
1 files changed, 135 insertions, 1 deletions
diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index 1952e82797c..1a74d562dca 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -4,7 +4,7 @@
* Author:
* Colin Cross <ccross@android.com>
*
- * Copyright (C) 2010, NVIDIA Corporation
+ * Copyright (C) 2010,2013, NVIDIA Corporation
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -18,11 +18,14 @@
*/
#include <linux/kernel.h>
+#include <linux/cpu_pm.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/irqchip/arm-gic.h>
+#include <linux/syscore_ops.h>
#include "board.h"
#include "iomap.h"
@@ -43,6 +46,7 @@
#define ICTLR_COP_IEP_CLASS 0x3c
#define FIRST_LEGACY_IRQ 32
+#define TEGRA_MAX_NUM_ICTLRS 5
#define SGI_MASK 0xFFFF
@@ -56,6 +60,16 @@ static void __iomem *ictlr_reg_base[] = {
IO_ADDRESS(TEGRA_QUINARY_ICTLR_BASE),
};
+#ifdef CONFIG_PM_SLEEP
+static u32 cop_ier[TEGRA_MAX_NUM_ICTLRS];
+static u32 cop_iep[TEGRA_MAX_NUM_ICTLRS];
+static u32 cpu_ier[TEGRA_MAX_NUM_ICTLRS];
+static u32 cpu_iep[TEGRA_MAX_NUM_ICTLRS];
+
+static u32 ictlr_wake_mask[TEGRA_MAX_NUM_ICTLRS];
+static void __iomem *tegra_gic_cpu_base;
+#endif
+
bool tegra_pending_sgi(void)
{
u32 pending_set;
@@ -125,6 +139,122 @@ static int tegra_retrigger(struct irq_data *d)
return 1;
}
+#ifdef CONFIG_PM_SLEEP
+static int tegra_set_wake(struct irq_data *d, unsigned int enable)
+{
+ u32 irq = d->irq;
+ u32 index, mask;
+
+ if (irq < FIRST_LEGACY_IRQ ||
+ irq >= FIRST_LEGACY_IRQ + num_ictlrs * 32)
+ return -EINVAL;
+
+ index = ((irq - FIRST_LEGACY_IRQ) / 32);
+ mask = BIT((irq - FIRST_LEGACY_IRQ) % 32);
+ if (enable)
+ ictlr_wake_mask[index] |= mask;
+ else
+ ictlr_wake_mask[index] &= ~mask;
+
+ return 0;
+}
+
+static int tegra_legacy_irq_suspend(void)
+{
+ unsigned long flags;
+ int i;
+
+ local_irq_save(flags);
+ for (i = 0; i < num_ictlrs; i++) {
+ void __iomem *ictlr = ictlr_reg_base[i];
+ /* Save interrupt state */
+ cpu_ier[i] = readl_relaxed(ictlr + ICTLR_CPU_IER);
+ cpu_iep[i] = readl_relaxed(ictlr + ICTLR_CPU_IEP_CLASS);
+ cop_ier[i] = readl_relaxed(ictlr + ICTLR_COP_IER);
+ cop_iep[i] = readl_relaxed(ictlr + ICTLR_COP_IEP_CLASS);
+
+ /* Disable COP interrupts */
+ writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR);
+
+ /* Disable CPU interrupts */
+ writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR);
+
+ /* Enable the wakeup sources of ictlr */
+ writel_relaxed(ictlr_wake_mask[i], ictlr + ICTLR_CPU_IER_SET);
+ }
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static void tegra_legacy_irq_resume(void)
+{
+ unsigned long flags;
+ int i;
+
+ local_irq_save(flags);
+ for (i = 0; i < num_ictlrs; i++) {
+ void __iomem *ictlr = ictlr_reg_base[i];
+ writel_relaxed(cpu_iep[i], ictlr + ICTLR_CPU_IEP_CLASS);
+ writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR);
+ writel_relaxed(cpu_ier[i], ictlr + ICTLR_CPU_IER_SET);
+ writel_relaxed(cop_iep[i], ictlr + ICTLR_COP_IEP_CLASS);
+ writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR);
+ writel_relaxed(cop_ier[i], ictlr + ICTLR_COP_IER_SET);
+ }
+ local_irq_restore(flags);
+}
+
+static struct syscore_ops tegra_legacy_irq_syscore_ops = {
+ .suspend = tegra_legacy_irq_suspend,
+ .resume = tegra_legacy_irq_resume,
+};
+
+int tegra_legacy_irq_syscore_init(void)
+{
+ register_syscore_ops(&tegra_legacy_irq_syscore_ops);
+
+ return 0;
+}
+
+static int tegra_gic_notifier(struct notifier_block *self,
+ unsigned long cmd, void *v)
+{
+ switch (cmd) {
+ case CPU_PM_ENTER:
+ writel_relaxed(0x1E0, tegra_gic_cpu_base + GIC_CPU_CTRL);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block tegra_gic_notifier_block = {
+ .notifier_call = tegra_gic_notifier,
+};
+
+static const struct of_device_id tegra114_dt_gic_match[] __initconst = {
+ { .compatible = "arm,cortex-a15-gic" },
+ { }
+};
+
+static void tegra114_gic_cpu_pm_registration(void)
+{
+ struct device_node *dn;
+
+ dn = of_find_matching_node(NULL, tegra114_dt_gic_match);
+ if (!dn)
+ return;
+
+ tegra_gic_cpu_base = of_iomap(dn, 1);
+
+ cpu_pm_register_notifier(&tegra_gic_notifier_block);
+}
+#else
+#define tegra_set_wake NULL
+static void tegra114_gic_cpu_pm_registration(void) { }
+#endif
+
void __init tegra_init_irq(void)
{
int i;
@@ -150,6 +280,8 @@ void __init tegra_init_irq(void)
gic_arch_extn.irq_mask = tegra_mask;
gic_arch_extn.irq_unmask = tegra_unmask;
gic_arch_extn.irq_retrigger = tegra_retrigger;
+ gic_arch_extn.irq_set_wake = tegra_set_wake;
+ gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND;
/*
* Check if there is a devicetree present, since the GIC will be
@@ -158,4 +290,6 @@ void __init tegra_init_irq(void)
if (!of_have_populated_dt())
gic_init(0, 29, distbase,
IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100));
+
+ tegra114_gic_cpu_pm_registration();
}