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

Commit 4770b12d authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "irqchip/gic-v2: implement suspend and resume"

parents 02645d3a fc8e11b2
Loading
Loading
Loading
Loading
+148 −0
Original line number Diff line number Diff line
@@ -42,6 +42,9 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/arm-gic.h>
#include <linux/msm_rtb.h>
#ifdef CONFIG_PM
#include <linux/syscore_ops.h>
#endif

#include <asm/cputype.h>
#include <asm/irq.h>
@@ -90,6 +93,11 @@ struct gic_chip_data {
#ifdef CONFIG_GIC_NON_BANKED
	void __iomem *(*get_base)(union gic_base *);
#endif
#ifdef CONFIG_PM
	unsigned int irq_offset;
	unsigned int wakeup_irqs[32];
	unsigned int enabled_irqs[32];
#endif
};

#ifdef CONFIG_BL_SWITCHER
@@ -228,6 +236,116 @@ static void gic_unmask_irq(struct irq_data *d)
	gic_poke_irq(d, GIC_DIST_ENABLE_SET);
}

#ifdef CONFIG_PM

static DEFINE_RAW_SPINLOCK(irq_controller_lock);

#ifndef MAX_GIC_NR
#define MAX_GIC_NR	1
#endif

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_show_resume_irq(struct gic_chip_data *gic)
{
	unsigned int i;
	u32 enabled;
	u32 pending[32];
	void __iomem *base = gic_data_dist_base(gic);

	raw_spin_lock(&irq_controller_lock);
	for (i = 0; i * 32 < gic->gic_irqs; i++) {
		enabled = readl_relaxed(base + GIC_DIST_ENABLE_CLEAR + i * 4);
		pending[i] = readl_relaxed(base + GIC_DIST_PENDING_SET + i * 4);
		pending[i] &= enabled;
	}
	raw_spin_unlock(&irq_controller_lock);

	for (i = find_first_bit((unsigned long *)pending, gic->gic_irqs);
		i < gic->gic_irqs;
		i = find_next_bit((unsigned long *)pending,
				gic->gic_irqs, i+1)) {
		unsigned int irq = irq_find_mapping(gic->domain,
						i + gic->irq_offset);
		struct irq_desc *desc = irq_to_desc(irq);
		const char *name = "null";

		if (desc == NULL)
			name = "stray irq";
		else if (desc->action && desc->action->name)
			name = desc->action->name;

		pr_warn("%s: %d triggered %s\n", __func__,
					i + gic->irq_offset, name);
	}
}

static void gic_resume_one(struct gic_chip_data *gic)
{
	unsigned int i;
	void __iomem *base = gic_data_dist_base(gic);

	gic_show_resume_irq(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)
{
	writel_relaxed(gic_irq(d), gic_cpu_base(d) + GIC_CPU_EOI);
@@ -351,6 +469,30 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
}
#endif

#ifdef CONFIG_PM
static int gic_set_wake(struct irq_data *d, unsigned int on)
{
	int ret = 0;

	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);

	return ret;
}
#endif

static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
	u32 irqstat, irqnr;
@@ -425,9 +567,15 @@ static const struct irq_chip gic_chip = {
	.irq_set_type		= gic_set_type,
	.irq_get_irqchip_state	= gic_irq_get_irqchip_state,
	.irq_set_irqchip_state	= gic_irq_set_irqchip_state,
#ifdef CONFIG_PM
	.irq_set_wake		= gic_set_wake,
	.flags			= IRQCHIP_SET_TYPE_MASKED |
				  IRQCHIP_MASK_ON_SUSPEND,
#else
	.flags			= IRQCHIP_SET_TYPE_MASKED |
				  IRQCHIP_SKIP_SET_WAKE |
				  IRQCHIP_MASK_ON_SUSPEND,
#endif
};

void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)