Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[cascardo/linux.git] / drivers / irqchip / irq-gic-v3.c
index a0698b4..aa17ae8 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #include <linux/cpu.h>
+#include <linux/cpu_pm.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/of.h>
@@ -155,7 +156,7 @@ static void gic_enable_sre(void)
                pr_err("GIC: unable to set SRE (disabled at EL2), panic ahead\n");
 }
 
-static void gic_enable_redist(void)
+static void gic_enable_redist(bool enable)
 {
        void __iomem *rbase;
        u32 count = 1000000;    /* 1s! */
@@ -163,20 +164,30 @@ static void gic_enable_redist(void)
 
        rbase = gic_data_rdist_rd_base();
 
-       /* Wake up this CPU redistributor */
        val = readl_relaxed(rbase + GICR_WAKER);
-       val &= ~GICR_WAKER_ProcessorSleep;
+       if (enable)
+               /* Wake up this CPU redistributor */
+               val &= ~GICR_WAKER_ProcessorSleep;
+       else
+               val |= GICR_WAKER_ProcessorSleep;
        writel_relaxed(val, rbase + GICR_WAKER);
 
-       while (readl_relaxed(rbase + GICR_WAKER) & GICR_WAKER_ChildrenAsleep) {
-               count--;
-               if (!count) {
-                       pr_err_ratelimited("redist didn't wake up...\n");
-                       return;
-               }
+       if (!enable) {          /* Check that GICR_WAKER is writeable */
+               val = readl_relaxed(rbase + GICR_WAKER);
+               if (!(val & GICR_WAKER_ProcessorSleep))
+                       return; /* No PM support in this redistributor */
+       }
+
+       while (count--) {
+               val = readl_relaxed(rbase + GICR_WAKER);
+               if (enable ^ (val & GICR_WAKER_ChildrenAsleep))
+                       break;
                cpu_relax();
                udelay(1);
        };
+       if (!count)
+               pr_err_ratelimited("redistributor failed to %s...\n",
+                                  enable ? "wakeup" : "sleep");
 }
 
 /*
@@ -261,14 +272,13 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
                irqnr = gic_read_iar();
 
                if (likely(irqnr > 15 && irqnr < 1020)) {
-                       u64 irq = irq_find_mapping(gic_data.domain, irqnr);
-                       if (likely(irq)) {
-                               handle_IRQ(irq, regs);
-                               continue;
+                       int err;
+                       err = handle_domain_irq(gic_data.domain, irqnr, regs);
+                       if (err) {
+                               WARN_ONCE(true, "Unexpected SPI received!\n");
+                               gic_write_eoir(irqnr);
                        }
-
-                       WARN_ONCE(true, "Unexpected SPI received!\n");
-                       gic_write_eoir(irqnr);
+                       continue;
                }
                if (irqnr < 16) {
                        gic_write_eoir(irqnr);
@@ -360,6 +370,21 @@ static int gic_populate_rdist(void)
        return -ENODEV;
 }
 
+static void gic_cpu_sys_reg_init(void)
+{
+       /* Enable system registers */
+       gic_enable_sre();
+
+       /* Set priority mask register */
+       gic_write_pmr(DEFAULT_PMR_VALUE);
+
+       /* EOI deactivates interrupt too (mode 0) */
+       gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop_dir);
+
+       /* ... and let's hit the road... */
+       gic_write_grpen1(1);
+}
+
 static void gic_cpu_init(void)
 {
        void __iomem *rbase;
@@ -368,23 +393,14 @@ static void gic_cpu_init(void)
        if (gic_populate_rdist())
                return;
 
-       gic_enable_redist();
+       gic_enable_redist(true);
 
        rbase = gic_data_rdist_sgi_base();
 
        gic_cpu_config(rbase, gic_redist_wait_for_rwp);
 
-       /* Enable system registers */
-       gic_enable_sre();
-
-       /* Set priority mask register */
-       gic_write_pmr(DEFAULT_PMR_VALUE);
-
-       /* EOI deactivates interrupt too (mode 0) */
-       gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop_dir);
-
-       /* ... and let's hit the road... */
-       gic_write_grpen1(1);
+       /* initialise system registers */
+       gic_cpu_sys_reg_init();
 }
 
 #ifdef CONFIG_SMP
@@ -533,6 +549,33 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
 #define gic_smp_init()         do { } while(0)
 #endif
 
+#ifdef CONFIG_CPU_PM
+static int gic_cpu_pm_notifier(struct notifier_block *self,
+                              unsigned long cmd, void *v)
+{
+       if (cmd == CPU_PM_EXIT) {
+               gic_enable_redist(true);
+               gic_cpu_sys_reg_init();
+       } else if (cmd == CPU_PM_ENTER) {
+               gic_write_grpen1(0);
+               gic_enable_redist(false);
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block gic_cpu_pm_notifier_block = {
+       .notifier_call = gic_cpu_pm_notifier,
+};
+
+static void gic_cpu_pm_init(void)
+{
+       cpu_pm_register_notifier(&gic_cpu_pm_notifier_block);
+}
+
+#else
+static inline void gic_cpu_pm_init(void) { }
+#endif /* CONFIG_CPU_PM */
+
 static struct irq_chip gic_chip = {
        .name                   = "GICv3",
        .irq_mask               = gic_mask_irq,
@@ -672,6 +715,7 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
        gic_smp_init();
        gic_dist_init();
        gic_cpu_init();
+       gic_cpu_pm_init();
 
        return 0;