diff options
Diffstat (limited to 'drivers/irqchip')
-rw-r--r-- | drivers/irqchip/irq-gic-v3.c | 87 |
1 files changed, 66 insertions, 21 deletions
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 9e31449..7247a3c 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -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"); } /* @@ -372,6 +383,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; @@ -380,23 +406,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 @@ -532,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, @@ -671,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; |