X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=kernel%2Fdrivers%2Firqchip%2Firq-gic-v3.c;h=e33c729b9f48d58b89727103f3a0de1be61cdf77;hb=HEAD;hp=49875adb6b44bd4c293e3f7a95d422a40aafd3b5;hpb=9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00;p=kvmfornfv.git diff --git a/kernel/drivers/irqchip/irq-gic-v3.c b/kernel/drivers/irqchip/irq-gic-v3.c index 49875adb6..e33c729b9 100644 --- a/kernel/drivers/irqchip/irq-gic-v3.c +++ b/kernel/drivers/irqchip/irq-gic-v3.c @@ -25,14 +25,15 @@ #include #include +#include #include #include #include #include +#include #include "irq-gic-common.h" -#include "irqchip.h" struct redist_region { void __iomem *redist_base; @@ -50,6 +51,7 @@ struct gic_chip_data { }; static struct gic_chip_data gic_data __read_mostly; +static struct static_key supports_deactivate = STATIC_KEY_INIT_TRUE; #define gic_data_rdist() (this_cpu_ptr(gic_data.rdists.rdist)) #define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base) @@ -106,57 +108,17 @@ static void gic_redist_wait_for_rwp(void) gic_do_wait_for_rwp(gic_data_rdist_rd_base()); } -/* Low level accessors */ -static u64 __maybe_unused gic_read_iar(void) -{ - u64 irqstat; - - asm volatile("mrs_s %0, " __stringify(ICC_IAR1_EL1) : "=r" (irqstat)); - return irqstat; -} - -static void __maybe_unused gic_write_pmr(u64 val) -{ - asm volatile("msr_s " __stringify(ICC_PMR_EL1) ", %0" : : "r" (val)); -} +#ifdef CONFIG_ARM64 +static DEFINE_STATIC_KEY_FALSE(is_cavium_thunderx); -static void __maybe_unused gic_write_ctlr(u64 val) -{ - asm volatile("msr_s " __stringify(ICC_CTLR_EL1) ", %0" : : "r" (val)); - isb(); -} - -static void __maybe_unused gic_write_grpen1(u64 val) -{ - asm volatile("msr_s " __stringify(ICC_GRPEN1_EL1) ", %0" : : "r" (val)); - isb(); -} - -static void __maybe_unused gic_write_sgi1r(u64 val) -{ - asm volatile("msr_s " __stringify(ICC_SGI1R_EL1) ", %0" : : "r" (val)); -} - -static void gic_enable_sre(void) +static u64 __maybe_unused gic_read_iar(void) { - u64 val; - - asm volatile("mrs_s %0, " __stringify(ICC_SRE_EL1) : "=r" (val)); - val |= ICC_SRE_EL1_SRE; - asm volatile("msr_s " __stringify(ICC_SRE_EL1) ", %0" : : "r" (val)); - isb(); - - /* - * Need to check that the SRE bit has actually been set. If - * not, it means that SRE is disabled at EL2. We're going to - * die painfully, and there is nothing we can do about it. - * - * Kindly inform the luser. - */ - asm volatile("mrs_s %0, " __stringify(ICC_SRE_EL1) : "=r" (val)); - if (!(val & ICC_SRE_EL1_SRE)) - pr_err("GIC: unable to set SRE (disabled at EL2), panic ahead\n"); + if (static_branch_unlikely(&is_cavium_thunderx)) + return gic_read_iar_cavium_thunderx(); + else + return gic_read_iar_common(); } +#endif static void gic_enable_redist(bool enable) { @@ -180,7 +142,7 @@ static void gic_enable_redist(bool enable) return; /* No PM support in this redistributor */ } - while (count--) { + while (--count) { val = readl_relaxed(rbase + GICR_WAKER); if (enable ^ (val & GICR_WAKER_ChildrenAsleep)) break; @@ -231,6 +193,21 @@ static void gic_mask_irq(struct irq_data *d) gic_poke_irq(d, GICD_ICENABLER); } +static void gic_eoimode1_mask_irq(struct irq_data *d) +{ + gic_mask_irq(d); + /* + * When masking a forwarded interrupt, make sure it is + * deactivated as well. + * + * This ensures that an interrupt that is getting + * disabled/masked will not get "stuck", because there is + * noone to deactivate it (guest is being terminated). + */ + if (irqd_is_forwarded_to_vcpu(d)) + gic_poke_irq(d, GICD_ICACTIVER); +} + static void gic_unmask_irq(struct irq_data *d) { gic_poke_irq(d, GICD_ISENABLER); @@ -296,6 +273,17 @@ static void gic_eoi_irq(struct irq_data *d) gic_write_eoir(gic_irq(d)); } +static void gic_eoimode1_eoi_irq(struct irq_data *d) +{ + /* + * No need to deactivate an LPI, or an interrupt that + * is is getting forwarded to a vcpu. + */ + if (gic_irq(d) >= 8192 || irqd_is_forwarded_to_vcpu(d)) + return; + gic_write_dir(gic_irq(d)); +} + static int gic_set_type(struct irq_data *d, unsigned int type) { unsigned int irq = gic_irq(d); @@ -322,11 +310,20 @@ static int gic_set_type(struct irq_data *d, unsigned int type) return gic_configure_irq(irq, type, base, rwp_wait); } -static u64 gic_mpidr_to_affinity(u64 mpidr) +static int gic_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu) +{ + if (vcpu) + irqd_set_forwarded_to_vcpu(d); + else + irqd_clr_forwarded_to_vcpu(d); + return 0; +} + +static u64 gic_mpidr_to_affinity(unsigned long mpidr) { u64 aff; - aff = (MPIDR_AFFINITY_LEVEL(mpidr, 3) << 32 | + aff = ((u64)MPIDR_AFFINITY_LEVEL(mpidr, 3) << 32 | MPIDR_AFFINITY_LEVEL(mpidr, 2) << 16 | MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8 | MPIDR_AFFINITY_LEVEL(mpidr, 0)); @@ -336,23 +333,41 @@ static u64 gic_mpidr_to_affinity(u64 mpidr) static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) { - u64 irqnr; + u32 irqnr; do { irqnr = gic_read_iar(); if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) { int err; + + if (static_key_true(&supports_deactivate)) + gic_write_eoir(irqnr); + err = handle_domain_irq(gic_data.domain, irqnr, regs); if (err) { WARN_ONCE(true, "Unexpected interrupt received!\n"); - gic_write_eoir(irqnr); + if (static_key_true(&supports_deactivate)) { + if (irqnr < 8192) + gic_write_dir(irqnr); + } else { + gic_write_eoir(irqnr); + } } continue; } if (irqnr < 16) { gic_write_eoir(irqnr); + if (static_key_true(&supports_deactivate)) + gic_write_dir(irqnr); #ifdef CONFIG_SMP + /* + * Unlike GICv2, we don't need an smp_rmb() here. + * The control dependency from gic_read_iar to + * the ISB in gic_write_eoir is enough to ensure + * that any shared data read by handle_IPI will + * be read after the ACK. + */ handle_IPI(irqnr, regs); #else WARN_ONCE(true, "Unexpected SGI received!\n"); @@ -372,6 +387,15 @@ static void __init gic_dist_init(void) writel_relaxed(0, base + GICD_CTLR); gic_dist_wait_for_rwp(); + /* + * Configure SPIs as non-secure Group-1. This will only matter + * if the GIC only has a single security state. This will not + * do the right thing if the kernel is running in secure mode, + * but that's not the intended use case anyway. + */ + for (i = 32; i < gic_data.irq_nr; i += 32) + writel_relaxed(~0, base + GICD_IGROUPR + i / 8); + gic_dist_config(base, gic_data.irq_nr, gic_dist_wait_for_rwp); /* Enable distributor with ARE, Group1 */ @@ -384,12 +408,12 @@ static void __init gic_dist_init(void) */ affinity = gic_mpidr_to_affinity(cpu_logical_map(smp_processor_id())); for (i = 32; i < gic_data.irq_nr; i++) - writeq_relaxed(affinity, base + GICD_IROUTER + i * 8); + gic_write_irouter(affinity, base + GICD_IROUTER + i * 8); } static int gic_populate_rdist(void) { - u64 mpidr = cpu_logical_map(smp_processor_id()); + unsigned long mpidr = cpu_logical_map(smp_processor_id()); u64 typer; u32 aff; int i; @@ -415,15 +439,14 @@ static int gic_populate_rdist(void) } do { - typer = readq_relaxed(ptr + GICR_TYPER); + typer = gic_read_typer(ptr + GICR_TYPER); if ((typer >> 32) == aff) { u64 offset = ptr - gic_data.redist_regions[i].redist_base; gic_data_rdist_rd_base() = ptr; gic_data_rdist()->phys_base = gic_data.redist_regions[i].phys_base + offset; - pr_info("CPU%d: found redistributor %llx region %d:%pa\n", - smp_processor_id(), - (unsigned long long)mpidr, - i, &gic_data_rdist()->phys_base); + pr_info("CPU%d: found redistributor %lx region %d:%pa\n", + smp_processor_id(), mpidr, i, + &gic_data_rdist()->phys_base); return 0; } @@ -438,21 +461,33 @@ static int gic_populate_rdist(void) } /* We couldn't even deal with ourselves... */ - WARN(true, "CPU%d: mpidr %llx has no re-distributor!\n", - smp_processor_id(), (unsigned long long)mpidr); + WARN(true, "CPU%d: mpidr %lx has no re-distributor!\n", + smp_processor_id(), mpidr); return -ENODEV; } static void gic_cpu_sys_reg_init(void) { - /* Enable system registers */ - gic_enable_sre(); + /* + * Need to check that the SRE bit has actually been set. If + * not, it means that SRE is disabled at EL2. We're going to + * die painfully, and there is nothing we can do about it. + * + * Kindly inform the luser. + */ + if (!gic_enable_sre()) + pr_err("GIC: unable to set SRE (disabled at EL2), panic ahead\n"); /* Set priority mask register */ gic_write_pmr(DEFAULT_PMR_VALUE); - /* EOI deactivates interrupt too (mode 0) */ - gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop_dir); + if (static_key_true(&supports_deactivate)) { + /* EOI drops priority only (mode 1) */ + gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop); + } else { + /* EOI deactivates interrupt too (mode 0) */ + gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop_dir); + } /* ... and let's hit the road... */ gic_write_grpen1(1); @@ -475,6 +510,9 @@ static void gic_cpu_init(void) rbase = gic_data_rdist_sgi_base(); + /* Configure SGIs/PPIs as non-secure Group-1 */ + writel_relaxed(~0, rbase + GICR_IGROUPR0); + gic_cpu_config(rbase, gic_redist_wait_for_rwp); /* Give LPIs a spin */ @@ -504,10 +542,10 @@ static struct notifier_block gic_cpu_notifier = { }; static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask, - u64 cluster_id) + unsigned long cluster_id) { - int cpu = *base_cpu; - u64 mpidr = cpu_logical_map(cpu); + int next_cpu, cpu = *base_cpu; + unsigned long mpidr = cpu_logical_map(cpu); u16 tlist = 0; while (cpu < nr_cpu_ids) { @@ -520,9 +558,10 @@ static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask, tlist |= 1 << (mpidr & 0xf); - cpu = cpumask_next(cpu, mask); - if (cpu >= nr_cpu_ids) + next_cpu = cpumask_next(cpu, mask); + if (next_cpu >= nr_cpu_ids) goto out; + cpu = next_cpu; mpidr = cpu_logical_map(cpu); @@ -568,7 +607,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) smp_wmb(); for_each_cpu(cpu, mask) { - u64 cluster_id = cpu_logical_map(cpu) & ~0xffUL; + unsigned long cluster_id = cpu_logical_map(cpu) & ~0xffUL; u16 tlist; tlist = gic_compute_target_list(&cpu, mask, cluster_id); @@ -604,7 +643,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, reg = gic_dist_base(d) + GICD_IROUTER + (gic_irq(d) * 8); val = gic_mpidr_to_affinity(cpu_logical_map(cpu)); - writeq_relaxed(val, reg); + gic_write_irouter(val, reg); /* * If the interrupt was enabled, enabled it again. Otherwise, @@ -658,6 +697,20 @@ static struct irq_chip gic_chip = { .irq_set_affinity = gic_set_affinity, .irq_get_irqchip_state = gic_irq_get_irqchip_state, .irq_set_irqchip_state = gic_irq_set_irqchip_state, + .flags = IRQCHIP_SET_TYPE_MASKED, +}; + +static struct irq_chip gic_eoimode1_chip = { + .name = "GICv3", + .irq_mask = gic_eoimode1_mask_irq, + .irq_unmask = gic_unmask_irq, + .irq_eoi = gic_eoimode1_eoi_irq, + .irq_set_type = gic_set_type, + .irq_set_affinity = gic_set_affinity, + .irq_get_irqchip_state = gic_irq_get_irqchip_state, + .irq_set_irqchip_state = gic_irq_set_irqchip_state, + .irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity, + .flags = IRQCHIP_SET_TYPE_MASKED, }; #define GIC_ID_NR (1U << gic_data.rdists.id_bits) @@ -665,6 +718,11 @@ static struct irq_chip gic_chip = { static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) { + struct irq_chip *chip = &gic_chip; + + if (static_key_true(&supports_deactivate)) + chip = &gic_eoimode1_chip; + /* SGIs are private to the core kernel */ if (hw < 16) return -EPERM; @@ -678,54 +736,55 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, /* PPIs */ if (hw < 32) { irq_set_percpu_devid(irq); - irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, + irq_domain_set_info(d, irq, hw, chip, d->host_data, handle_percpu_devid_irq, NULL, NULL); - set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); + irq_set_status_flags(irq, IRQ_NOAUTOEN); } /* SPIs */ if (hw >= 32 && hw < gic_data.irq_nr) { - irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, + irq_domain_set_info(d, irq, hw, chip, d->host_data, handle_fasteoi_irq, NULL, NULL); - set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + irq_set_probe(irq); } /* LPIs */ if (hw >= 8192 && hw < GIC_ID_NR) { if (!gic_dist_supports_lpis()) return -EPERM; - irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, + irq_domain_set_info(d, irq, hw, chip, d->host_data, handle_fasteoi_irq, NULL, NULL); - set_irq_flags(irq, IRQF_VALID); } return 0; } -static int gic_irq_domain_xlate(struct irq_domain *d, - struct device_node *controller, - const u32 *intspec, unsigned int intsize, - unsigned long *out_hwirq, unsigned int *out_type) +static int gic_irq_domain_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) { - if (d->of_node != controller) - return -EINVAL; - if (intsize < 3) - return -EINVAL; + if (is_of_node(fwspec->fwnode)) { + if (fwspec->param_count < 3) + return -EINVAL; - switch(intspec[0]) { - case 0: /* SPI */ - *out_hwirq = intspec[1] + 32; - break; - case 1: /* PPI */ - *out_hwirq = intspec[1] + 16; - break; - case GIC_IRQ_TYPE_LPI: /* LPI */ - *out_hwirq = intspec[1]; - break; - default: - return -EINVAL; + switch (fwspec->param[0]) { + case 0: /* SPI */ + *hwirq = fwspec->param[1] + 32; + break; + case 1: /* PPI */ + *hwirq = fwspec->param[1] + 16; + break; + case GIC_IRQ_TYPE_LPI: /* LPI */ + *hwirq = fwspec->param[1]; + break; + default: + return -EINVAL; + } + + *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; + return 0; } - *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; - return 0; + return -EINVAL; } static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, @@ -734,10 +793,9 @@ static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, int i, ret; irq_hw_number_t hwirq; unsigned int type = IRQ_TYPE_NONE; - struct of_phandle_args *irq_data = arg; + struct irq_fwspec *fwspec = arg; - ret = gic_irq_domain_xlate(domain, irq_data->np, irq_data->args, - irq_data->args_count, &hwirq, &type); + ret = gic_irq_domain_translate(domain, fwspec, &hwirq, &type); if (ret) return ret; @@ -760,11 +818,19 @@ static void gic_irq_domain_free(struct irq_domain *domain, unsigned int virq, } static const struct irq_domain_ops gic_irq_domain_ops = { - .xlate = gic_irq_domain_xlate, + .translate = gic_irq_domain_translate, .alloc = gic_irq_domain_alloc, .free = gic_irq_domain_free, }; +static void gicv3_enable_quirks(void) +{ +#ifdef CONFIG_ARM64 + if (cpus_have_cap(ARM64_WORKAROUND_CAVIUM_23154)) + static_branch_enable(&is_cavium_thunderx); +#endif +} + static int __init gic_of_init(struct device_node *node, struct device_node *parent) { void __iomem *dist_base; @@ -819,11 +885,19 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare if (of_property_read_u64(node, "redistributor-stride", &redist_stride)) redist_stride = 0; + if (!is_hyp_mode_available()) + static_key_slow_dec(&supports_deactivate); + + if (static_key_true(&supports_deactivate)) + pr_info("GIC: Using split EOI/Deactivate mode\n"); + gic_data.dist_base = dist_base; gic_data.redist_regions = rdist_regs; gic_data.nr_redist_regions = nr_redist_regions; gic_data.redist_stride = redist_stride; + gicv3_enable_quirks(); + /* * Find out how many interrupts are supported. * The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI)