diff options
Diffstat (limited to 'drivers/irqchip/irq-gic.c')
-rw-r--r-- | drivers/irqchip/irq-gic.c | 79 |
1 files changed, 75 insertions, 4 deletions
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index cebd8efe651a..10b73d9bea78 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -98,6 +98,11 @@ static DEFINE_RAW_SPINLOCK(irq_controller_lock); static u8 gic_cpu_map[NR_GIC_CPU_IF] __read_mostly; static struct static_key supports_deactivate = STATIC_KEY_INIT_TRUE; +/* + * Supported arch specific GIC irq extension. + * Default make them NULL. + */ +extern struct irq_chip gic_arch_extn; #ifndef MAX_GIC_NR #define MAX_GIC_NR 1 @@ -182,7 +187,13 @@ static int gic_peek_irq(struct irq_data *d, u32 offset) static void gic_mask_irq(struct irq_data *d) { + unsigned long flags; + + raw_spin_lock_irqsave(&irq_controller_lock, flags); gic_poke_irq(d, GIC_DIST_ENABLE_CLEAR); + if (gic_arch_extn.irq_mask) + gic_arch_extn.irq_mask(d); + raw_spin_unlock_irqrestore(&irq_controller_lock, flags); } static void gic_eoimode1_mask_irq(struct irq_data *d) @@ -202,11 +213,23 @@ static void gic_eoimode1_mask_irq(struct irq_data *d) static void gic_unmask_irq(struct irq_data *d) { + unsigned long flags; + + raw_spin_lock_irqsave(&irq_controller_lock, flags); + if (gic_arch_extn.irq_unmask) + gic_arch_extn.irq_unmask(d); gic_poke_irq(d, GIC_DIST_ENABLE_SET); + raw_spin_unlock_irqrestore(&irq_controller_lock, flags); } static void gic_eoi_irq(struct irq_data *d) { + if (gic_arch_extn.irq_eoi) { + raw_spin_lock(&irq_controller_lock); + gic_arch_extn.irq_eoi(d); + raw_spin_unlock(&irq_controller_lock); + } + writel_relaxed(gic_irq(d), gic_cpu_base(d) + GIC_CPU_EOI); } @@ -272,6 +295,8 @@ static int gic_set_type(struct irq_data *d, unsigned int type) { void __iomem *base = gic_dist_base(d); unsigned int gicirq = gic_irq(d); + unsigned long flags; + int ret; /* Interrupt configuration for SGIs can't be changed */ if (gicirq < 16) @@ -282,7 +307,25 @@ static int gic_set_type(struct irq_data *d, unsigned int type) type != IRQ_TYPE_EDGE_RISING) return -EINVAL; - return gic_configure_irq(gicirq, type, base, NULL); + raw_spin_lock_irqsave(&irq_controller_lock, flags); + + if (gic_arch_extn.irq_set_type) + gic_arch_extn.irq_set_type(d, type); + + ret = gic_configure_irq(gicirq, type, base, NULL); + + raw_spin_unlock_irqrestore(&irq_controller_lock, flags); + + return ret; +} + +static int gic_retrigger(struct irq_data *d) +{ + if (gic_arch_extn.irq_retrigger) + return gic_arch_extn.irq_retrigger(d); + + /* the genirq layer expects 0 if we can't retrigger in hardware */ + return 0; } static int gic_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu) @@ -326,6 +369,21 @@ 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 = -ENXIO; + + if (gic_arch_extn.irq_set_wake) + ret = gic_arch_extn.irq_set_wake(d, on); + + return ret; +} + +#else +#define gic_set_wake NULL +#endif + static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) { u32 irqstat, irqnr; @@ -396,9 +454,11 @@ static struct irq_chip gic_chip = { .irq_unmask = gic_unmask_irq, .irq_eoi = gic_eoi_irq, .irq_set_type = gic_set_type, + .irq_retrigger = gic_retrigger, #ifdef CONFIG_SMP .irq_set_affinity = gic_set_affinity, #endif + .irq_set_wake = gic_set_wake, .irq_get_irqchip_state = gic_irq_get_irqchip_state, .irq_set_irqchip_state = gic_irq_set_irqchip_state, .flags = IRQCHIP_SET_TYPE_MASKED | @@ -707,7 +767,8 @@ static void gic_cpu_restore(unsigned int gic_nr) gic_cpu_if_up(&gic_data[gic_nr]); } -static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) +static int gic_notifier(struct notifier_block *self, unsigned long cmd, + void *aff_level) { int i; @@ -726,11 +787,20 @@ static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) gic_cpu_restore(i); break; case CPU_CLUSTER_PM_ENTER: - gic_dist_save(i); + /* + * Affinity level of the node + * eg: + * cpu level = 0 + * l2 level = 1 + * cci level = 2 + */ + if (!(unsigned long)aff_level) + gic_dist_save(i); break; case CPU_CLUSTER_PM_ENTER_FAILED: case CPU_CLUSTER_PM_EXIT: - gic_dist_restore(i); + if (!(unsigned long)aff_level) + gic_dist_restore(i); break; } } @@ -1147,6 +1217,7 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, pr_info("GIC: Using split EOI/Deactivate mode\n"); } + gic_chip.flags |= gic_arch_extn.flags; gic_dist_init(gic); gic_cpu_init(gic); gic_pm_init(gic); |