diff options
author | Thomas Petazzoni <thomas.petazzoni@free-electrons.com> | 2014-11-21 16:00:00 (GMT) |
---|---|---|
committer | Jason Cooper <jason@lakedaemon.net> | 2014-11-30 16:39:12 (GMT) |
commit | 0f077eb5cfaf453ad7379963a721b8c04f7c62a2 (patch) | |
tree | 850c36fc41868b79e82b00dbeabca6f99d950e9f /drivers/irqchip | |
parent | e92293a2a7edc68a37ee124b6665ca240fb2ce07 (diff) | |
download | linux-0f077eb5cfaf453ad7379963a721b8c04f7c62a2.tar.xz |
irqchip: armada-370-xp: Add suspend/resume support
This commit adds suspend/resume support to the irqchip driver used on
Armada XP platforms (amongst others). It does so by adding a set of
suspend/resume syscore_ops, that will respectively save and restore
the necessary registers to ensure interrupts continue to work after
resume.
It is worth mentioning that the affinity is lost during a
suspend/resume cycle, because when a secondary CPU is brought
off-line, all interrupts that are assigned to this CPU in terms of
affinity gets re-assigned to a still running CPU. Therefore, right
before entering suspend, all interrupts are assigned to the boot CPU.
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: linux-kernel@vger.kernel.org
Link: https://lkml.kernel.org/r/1416585613-2113-4-git-send-email-thomas.petazzoni@free-electrons.com
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
Diffstat (limited to 'drivers/irqchip')
-rw-r--r-- | drivers/irqchip/irq-armada-370-xp.c | 52 |
1 files changed, 52 insertions, 0 deletions
diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 3e238cd..4ec137b 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -26,6 +26,7 @@ #include <linux/of_pci.h> #include <linux/irqdomain.h> #include <linux/slab.h> +#include <linux/syscore_ops.h> #include <linux/msi.h> #include <asm/mach/arch.h> #include <asm/exception.h> @@ -66,6 +67,7 @@ static void __iomem *per_cpu_int_base; static void __iomem *main_int_base; static struct irq_domain *armada_370_xp_mpic_domain; +static u32 doorbell_mask_reg; #ifdef CONFIG_PCI_MSI static struct irq_domain *armada_370_xp_msi_domain; static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR); @@ -474,6 +476,54 @@ armada_370_xp_handle_irq(struct pt_regs *regs) } while (1); } +static int armada_370_xp_mpic_suspend(void) +{ + doorbell_mask_reg = readl(per_cpu_int_base + + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); + return 0; +} + +static void armada_370_xp_mpic_resume(void) +{ + int nirqs; + irq_hw_number_t irq; + + /* Re-enable interrupts */ + nirqs = (readl(main_int_base + ARMADA_370_XP_INT_CONTROL) >> 2) & 0x3ff; + for (irq = 0; irq < nirqs; irq++) { + struct irq_data *data; + int virq; + + virq = irq_linear_revmap(armada_370_xp_mpic_domain, irq); + if (virq == 0) + continue; + + if (irq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) + writel(irq, per_cpu_int_base + + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); + else + writel(irq, main_int_base + + ARMADA_370_XP_INT_SET_ENABLE_OFFS); + + data = irq_get_irq_data(virq); + if (!irqd_irq_disabled(data)) + armada_370_xp_irq_unmask(data); + } + + /* Reconfigure doorbells for IPIs and MSIs */ + writel(doorbell_mask_reg, + per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); + if (doorbell_mask_reg & IPI_DOORBELL_MASK) + writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); + if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK) + writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); +} + +struct syscore_ops armada_370_xp_mpic_syscore_ops = { + .suspend = armada_370_xp_mpic_suspend, + .resume = armada_370_xp_mpic_resume, +}; + static int __init armada_370_xp_mpic_of_init(struct device_node *node, struct device_node *parent) { @@ -530,6 +580,8 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, armada_370_xp_mpic_handle_cascade_irq); } + register_syscore_ops(&armada_370_xp_mpic_syscore_ops); + return 0; } |