irqchip/gic-v2: implement suspend and resume

Add gic pm functions and show wakeup interrupt.

Change-Id: Icbfd60d9ef47bb40409ca00aa413da09fd22899e
Signed-off-by: Yimin Peng <yiminp@codeaurora.org>
tirimbino
Yimin Peng 5 years ago
parent a933e78028
commit fc8e11b256
  1. 148
      drivers/irqchip/irq-gic.c

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

Loading…
Cancel
Save