Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 76330c4c authored by Abhijeet Dharmapurikar's avatar Abhijeet Dharmapurikar Committed by Yimin Peng
Browse files

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: default avatarAbhijeet Dharmapurikar <adharmap@codeaurora.org>
parent cb0e8e7c
Loading
Loading
Loading
Loading
+88 −0
Original line number Diff line number Diff line
@@ -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);