summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAbhijeet Dharmapurikar <adharmap@codeaurora.org>2010-08-11 17:18:28 -0700
committerYimin Peng <yiminp@codeaurora.org>2018-05-03 14:19:34 +0800
commit76330c4c308dfb9d89942737b21047d3260bfa3a (patch)
tree1b043aeb4dc38ce38b0935d05575b2018400b706
parentcb0e8e7c96590b4df7485c2c815fc131b7838325 (diff)
GIC: implement suspend and resume
While in suspend state, the system should not wake up due to triggering of a non wakeup interrupt. Implement suspend and resume functions to be called from power management code to switch enabled interrupts between wakeup set or normal set. Change-Id: Iaceae286707460eadc5f05c0baef72b43c942777 Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
-rw-r--r--drivers/irqchip/irq-gic.c88
1 files changed, 88 insertions, 0 deletions
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 10b73d9bea78..2ca62ef5ff36 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -41,6 +41,7 @@
#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/arm-gic.h>
+#include <linux/syscore_ops.h>
#include <asm/cputype.h>
#include <asm/irq.h>
@@ -69,6 +70,7 @@ union gic_base {
};
struct gic_chip_data {
+ unsigned int irq_offset;
union gic_base dist_base;
union gic_base cpu_base;
#ifdef CONFIG_CPU_PM
@@ -85,6 +87,10 @@ struct gic_chip_data {
#ifdef CONFIG_GIC_NON_BANKED
void __iomem *(*get_base)(union gic_base *);
#endif
+#ifdef CONFIG_PM
+ unsigned int wakeup_irqs[32];
+ unsigned int enabled_irqs[32];
+#endif
};
static DEFINE_RAW_SPINLOCK(irq_controller_lock);
@@ -222,6 +228,74 @@ static void gic_unmask_irq(struct irq_data *d)
raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
}
+#ifdef CONFIG_PM
+static int gic_suspend_one(struct gic_chip_data *gic)
+{
+ unsigned int i;
+ void __iomem *base = gic_data_dist_base(gic);
+
+ for (i = 0; i * 32 < gic->gic_irqs; i++) {
+ gic->enabled_irqs[i]
+ = readl_relaxed(base + GIC_DIST_ENABLE_SET + i * 4);
+ /* disable all of them */
+ writel_relaxed(0xffffffff,
+ base + GIC_DIST_ENABLE_CLEAR + i * 4);
+ /* enable the wakeup set */
+ writel_relaxed(gic->wakeup_irqs[i],
+ base + GIC_DIST_ENABLE_SET + i * 4);
+ }
+ /* make sure all gic setting finished */
+ mb();
+ return 0;
+}
+
+static int gic_suspend(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_GIC_NR; i++)
+ gic_suspend_one(&gic_data[i]);
+ return 0;
+}
+
+static void gic_resume_one(struct gic_chip_data *gic)
+{
+ unsigned int i;
+ void __iomem *base = gic_data_dist_base(gic);
+
+ for (i = 0; i * 32 < gic->gic_irqs; i++) {
+ /* disable all of them */
+ writel_relaxed(0xffffffff,
+ base + GIC_DIST_ENABLE_CLEAR + i * 4);
+ /* enable the enabled set */
+ writel_relaxed(gic->enabled_irqs[i],
+ base + GIC_DIST_ENABLE_SET + i * 4);
+ }
+ /* make sure all gic setting finished */
+ mb();
+}
+
+static void gic_resume(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_GIC_NR; i++)
+ gic_resume_one(&gic_data[i]);
+}
+
+static struct syscore_ops gic_syscore_ops = {
+ .suspend = gic_suspend,
+ .resume = gic_resume,
+};
+
+static int __init gic_init_sys(void)
+{
+ register_syscore_ops(&gic_syscore_ops);
+ return 0;
+}
+arch_initcall(gic_init_sys);
+#endif
+
static void gic_eoi_irq(struct irq_data *d)
{
if (gic_arch_extn.irq_eoi) {
@@ -373,6 +447,20 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
static int gic_set_wake(struct irq_data *d, unsigned int on)
{
int ret = -ENXIO;
+ unsigned int reg_offset, bit_offset;
+ unsigned int gicirq = gic_irq(d);
+ struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d);
+
+ /* per-cpu interrupts cannot be wakeup interrupts */
+ WARN_ON(gicirq < 32);
+
+ reg_offset = gicirq / 32;
+ bit_offset = gicirq % 32;
+
+ if (on)
+ gic_data->wakeup_irqs[reg_offset] |= 1 << bit_offset;
+ else
+ gic_data->wakeup_irqs[reg_offset] &= ~(1 << bit_offset);
if (gic_arch_extn.irq_set_wake)
ret = gic_arch_extn.irq_set_wake(d, on);