浏览代码

ARM: tegra: irq: add wake up handling

Add the wake up handling for legacy irq controller, and using
IRQCHIP_MASK_ON_SUSPEND for wake irq handling.

Based on the work by:
Varun Wadekar <vwadekar@nvidia.com>

Signed-off-by: Joseph Lo <josephl@nvidia.com>
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Joseph Lo 12 年之前
父节点
当前提交
e307cc8941
共有 3 个文件被更改,包括 103 次插入1 次删除
  1. 2 0
      arch/arm/mach-tegra/common.c
  2. 95 1
      arch/arm/mach-tegra/irq.c
  3. 6 0
      arch/arm/mach-tegra/irq.h

+ 2 - 0
arch/arm/mach-tegra/common.c

@@ -33,6 +33,7 @@
 #include "common.h"
 #include "fuse.h"
 #include "iomap.h"
+#include "irq.h"
 #include "pmc.h"
 #include "apbio.h"
 #include "sleep.h"
@@ -64,6 +65,7 @@ void __init tegra_dt_init_irq(void)
 	tegra_pmc_init();
 	tegra_init_irq();
 	irqchip_init();
+	tegra_legacy_irq_syscore_init();
 }
 #endif
 

+ 95 - 1
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
@@ -23,6 +23,7 @@
 #include <linux/io.h>
 #include <linux/of.h>
 #include <linux/irqchip/arm-gic.h>
+#include <linux/syscore_ops.h>
 
 #include "board.h"
 #include "iomap.h"
@@ -43,6 +44,7 @@
 #define ICTLR_COP_IEP_CLASS	0x3c
 
 #define FIRST_LEGACY_IRQ 32
+#define TEGRA_MAX_NUM_ICTLRS	5
 
 #define SGI_MASK 0xFFFF
 
@@ -56,6 +58,15 @@ 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];
+#endif
+
 bool tegra_pending_sgi(void)
 {
 	u32 pending_set;
@@ -125,6 +136,87 @@ 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;
+}
+#else
+#define tegra_set_wake NULL
+#endif
+
 void __init tegra_init_irq(void)
 {
 	int i;
@@ -150,6 +242,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

+ 6 - 0
arch/arm/mach-tegra/irq.h

@@ -19,4 +19,10 @@
 
 bool tegra_pending_sgi(void);
 
+#ifdef CONFIG_PM_SLEEP
+int tegra_legacy_irq_syscore_init(void);
+#else
+static inline int tegra_legacy_irq_syscore_init(void) { return 0; }
+#endif
+
 #endif