diff options
author | Paul Burton <paul.burton@imgtec.com> | 2017-04-20 10:07:35 +0100 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2017-04-20 16:07:02 +0200 |
commit | b87281e7f205eda08c911fdacd27d4d2f01daa09 (patch) | |
tree | fa2b609c883db0162b14da5eefac3d0945d8cca6 | |
parent | f8dcd9e81797ae24acc44c84f0eb3b9e6cee9791 (diff) |
irqchip/mips-gic: Remove device IRQ domain
In commit c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain")
Qais indicates that he felt having a separate device IRQ domain was
cleaner, but along with everyone else I'm aware of touching this driver
I disagree.
Remove the separate device IRQ domain so that we simply have the main
GIC IRQ domain used for devices, and an IPI IRQ domain as a child. The
logic for handling the device interrupts & IPIs is cleanly separated
into the appropriate domain ops, making it much easier to reason about
what the driver is doing than the previous approach where the 2 child
domains had to call up to their parent, which had to handle both types
of interrupt & had all sorts of weird & wonderful duplication or
outright clobbering of setup performed by multiple domains.
Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Signed-off-by: Matt Redfearn <matt.redfearn@imgtec.com>
Cc: linux-mips@linux-mips.org
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Link: http://lkml.kernel.org/r/1492679256-14513-3-git-send-email-matt.redfearn@imgtec.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | drivers/irqchip/irq-mips-gic.c | 290 |
1 files changed, 93 insertions, 197 deletions
diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index db058e10df00..eb448a1d57b4 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c @@ -29,25 +29,12 @@ struct gic_pcpu_mask { DECLARE_BITMAP(pcpu_mask, GIC_MAX_INTRS); }; -struct gic_irq_spec { - enum { - GIC_DEVICE, - GIC_IPI - } type; - - union { - struct cpumask *ipimask; - unsigned int hwirq; - }; -}; - static unsigned long __gic_base_addr; static void __iomem *gic_base; static struct gic_pcpu_mask pcpu_masks[NR_CPUS]; static DEFINE_SPINLOCK(gic_lock); static struct irq_domain *gic_irq_domain; -static struct irq_domain *gic_dev_domain; static struct irq_domain *gic_ipi_domain; static int gic_shared_intrs; static int gic_vpes; @@ -694,132 +681,7 @@ static int gic_shared_irq_domain_map(struct irq_domain *d, unsigned int virq, return 0; } -static int gic_setup_dev_chip(struct irq_domain *d, unsigned int virq, - unsigned int hwirq) -{ - struct irq_chip *chip; - int err; - - if (hwirq >= GIC_SHARED_HWIRQ_BASE) { - err = irq_domain_set_hwirq_and_chip(d, virq, hwirq, - &gic_level_irq_controller, - NULL); - } else { - switch (GIC_HWIRQ_TO_LOCAL(hwirq)) { - case GIC_LOCAL_INT_TIMER: - case GIC_LOCAL_INT_PERFCTR: - case GIC_LOCAL_INT_FDC: - /* - * HACK: These are all really percpu interrupts, but - * the rest of the MIPS kernel code does not use the - * percpu IRQ API for them. - */ - chip = &gic_all_vpes_local_irq_controller; - irq_set_handler(virq, handle_percpu_irq); - break; - - default: - chip = &gic_local_irq_controller; - irq_set_handler(virq, handle_percpu_devid_irq); - irq_set_percpu_devid(virq); - break; - } - - err = irq_domain_set_hwirq_and_chip(d, virq, hwirq, - chip, NULL); - } - - return err; -} - -static int gic_irq_domain_alloc(struct irq_domain *d, unsigned int virq, - unsigned int nr_irqs, void *arg) -{ - struct gic_irq_spec *spec = arg; - irq_hw_number_t hwirq, base_hwirq; - int cpu, ret, i; - - if (spec->type == GIC_DEVICE) { - /* verify that shared irqs don't conflict with an IPI irq */ - if ((spec->hwirq >= GIC_SHARED_HWIRQ_BASE) && - test_bit(GIC_HWIRQ_TO_SHARED(spec->hwirq), ipi_resrv)) - return -EBUSY; - - return gic_setup_dev_chip(d, virq, spec->hwirq); - } else { - base_hwirq = find_first_bit(ipi_available, gic_shared_intrs); - if (base_hwirq == gic_shared_intrs) { - return -ENOMEM; - } - - /* check that we have enough space */ - for (i = base_hwirq; i < nr_irqs; i++) { - if (!test_bit(i, ipi_available)) - return -EBUSY; - } - bitmap_clear(ipi_available, base_hwirq, nr_irqs); - - /* map the hwirq for each cpu consecutively */ - i = 0; - for_each_cpu(cpu, spec->ipimask) { - hwirq = GIC_SHARED_TO_HWIRQ(base_hwirq + i); - - ret = irq_domain_set_hwirq_and_chip(d, virq + i, hwirq, - &gic_level_irq_controller, - NULL); - if (ret) - goto error; - - irq_set_handler(virq + i, handle_level_irq); - - ret = gic_shared_irq_domain_map(d, virq + i, hwirq, cpu); - if (ret) - goto error; - - i++; - } - - /* - * tell the parent about the base hwirq we allocated so it can - * set its own domain data - */ - spec->hwirq = base_hwirq; - } - - return 0; -error: - bitmap_set(ipi_available, base_hwirq, nr_irqs); - return ret; -} - -void gic_irq_domain_free(struct irq_domain *d, unsigned int virq, - unsigned int nr_irqs) -{ - irq_hw_number_t base_hwirq; - struct irq_data *data; - - data = irq_get_irq_data(virq); - if (!data) - return; - - base_hwirq = GIC_HWIRQ_TO_SHARED(irqd_to_hwirq(data)); - bitmap_set(ipi_available, base_hwirq, nr_irqs); -} - -int gic_irq_domain_match(struct irq_domain *d, struct device_node *node, - enum irq_domain_bus_token bus_token) -{ - /* this domain should'nt be accessed directly */ - return 0; -} - -static const struct irq_domain_ops gic_irq_domain_ops = { - .alloc = gic_irq_domain_alloc, - .free = gic_irq_domain_free, - .match = gic_irq_domain_match, -}; - -static int gic_dev_domain_xlate(struct irq_domain *d, struct device_node *ctrlr, +static int gic_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr, const u32 *intspec, unsigned int intsize, irq_hw_number_t *out_hwirq, unsigned int *out_type) @@ -838,58 +700,73 @@ static int gic_dev_domain_xlate(struct irq_domain *d, struct device_node *ctrlr, return 0; } -static int gic_dev_domain_alloc(struct irq_domain *d, unsigned int virq, +static int gic_irq_domain_alloc(struct irq_domain *d, unsigned int virq, unsigned int nr_irqs, void *arg) { struct irq_fwspec *fwspec = arg; - struct gic_irq_spec spec = { - .type = GIC_DEVICE, - }; - int i, ret; + irq_hw_number_t hwirq; + int err; - if (fwspec->param[0] == GIC_SHARED) - spec.hwirq = GIC_SHARED_TO_HWIRQ(fwspec->param[1]); - else - spec.hwirq = GIC_LOCAL_TO_HWIRQ(fwspec->param[1]); + if (fwspec->param[0] == GIC_SHARED) { + hwirq = GIC_SHARED_TO_HWIRQ(fwspec->param[1]); - ret = irq_domain_alloc_irqs_parent(d, virq, nr_irqs, &spec); - if (ret) - return ret; + /* verify that shared irqs don't conflict with an IPI irq */ + if (test_bit(GIC_HWIRQ_TO_SHARED(hwirq), ipi_resrv)) + return -EBUSY; - for (i = 0; i < nr_irqs; i++) { - ret = gic_setup_dev_chip(d, virq + i, spec.hwirq + i); - if (ret) - goto error; + err = irq_domain_set_hwirq_and_chip(d, virq, hwirq, + &gic_level_irq_controller, + NULL); + if (err) + return err; + + return gic_shared_irq_domain_map(d, virq, hwirq, 0); } - return 0; + hwirq = GIC_LOCAL_TO_HWIRQ(fwspec->param[1]); -error: - irq_domain_free_irqs_parent(d, virq, nr_irqs); - return ret; -} + switch (GIC_HWIRQ_TO_LOCAL(hwirq)) { + case GIC_LOCAL_INT_TIMER: + case GIC_LOCAL_INT_PERFCTR: + case GIC_LOCAL_INT_FDC: + /* + * HACK: These are all really percpu interrupts, but + * the rest of the MIPS kernel code does not use the + * percpu IRQ API for them. + */ + err = irq_domain_set_hwirq_and_chip(d, virq, hwirq, + &gic_all_vpes_local_irq_controller, + NULL); + if (err) + return err; -void gic_dev_domain_free(struct irq_domain *d, unsigned int virq, - unsigned int nr_irqs) -{ - /* no real allocation is done for dev irqs, so no need to free anything */ - return; + irq_set_handler(virq, handle_percpu_irq); + break; + + default: + err = irq_domain_set_hwirq_and_chip(d, virq, hwirq, + &gic_local_irq_controller, + NULL); + if (err) + return err; + + irq_set_handler(virq, handle_percpu_devid_irq); + irq_set_percpu_devid(virq); + break; + } + + return gic_local_irq_domain_map(d, virq, hwirq); } -static void gic_dev_domain_activate(struct irq_domain *domain, - struct irq_data *d) +void gic_irq_domain_free(struct irq_domain *d, unsigned int virq, + unsigned int nr_irqs) { - if (GIC_HWIRQ_TO_LOCAL(d->hwirq) < GIC_NUM_LOCAL_INTRS) - gic_local_irq_domain_map(domain, d->irq, d->hwirq); - else - gic_shared_irq_domain_map(domain, d->irq, d->hwirq, 0); } -static struct irq_domain_ops gic_dev_domain_ops = { - .xlate = gic_dev_domain_xlate, - .alloc = gic_dev_domain_alloc, - .free = gic_dev_domain_free, - .activate = gic_dev_domain_activate, +static const struct irq_domain_ops gic_irq_domain_ops = { + .xlate = gic_irq_domain_xlate, + .alloc = gic_irq_domain_alloc, + .free = gic_irq_domain_free, }; static int gic_ipi_domain_xlate(struct irq_domain *d, struct device_node *ctrlr, @@ -911,20 +788,32 @@ static int gic_ipi_domain_alloc(struct irq_domain *d, unsigned int virq, unsigned int nr_irqs, void *arg) { struct cpumask *ipimask = arg; - struct gic_irq_spec spec = { - .type = GIC_IPI, - .ipimask = ipimask - }; - int ret, i; + irq_hw_number_t hwirq, base_hwirq; + int cpu, ret, i; + + base_hwirq = find_first_bit(ipi_available, gic_shared_intrs); + if (base_hwirq == gic_shared_intrs) + return -ENOMEM; - ret = irq_domain_alloc_irqs_parent(d, virq, nr_irqs, &spec); - if (ret) - return ret; + /* check that we have enough space */ + for (i = base_hwirq; i < nr_irqs; i++) { + if (!test_bit(i, ipi_available)) + return -EBUSY; + } + bitmap_clear(ipi_available, base_hwirq, nr_irqs); + + /* map the hwirq for each cpu consecutively */ + i = 0; + for_each_cpu(cpu, ipimask) { + hwirq = GIC_SHARED_TO_HWIRQ(base_hwirq + i); + + ret = irq_domain_set_hwirq_and_chip(d, virq + i, hwirq, + &gic_edge_irq_controller, + NULL); + if (ret) + goto error; - /* the parent should have set spec.hwirq to the base_hwirq it allocated */ - for (i = 0; i < nr_irqs; i++) { - ret = irq_domain_set_hwirq_and_chip(d, virq + i, - GIC_SHARED_TO_HWIRQ(spec.hwirq + i), + ret = irq_domain_set_hwirq_and_chip(d->parent, virq + i, hwirq, &gic_edge_irq_controller, NULL); if (ret) @@ -933,18 +822,32 @@ static int gic_ipi_domain_alloc(struct irq_domain *d, unsigned int virq, ret = irq_set_irq_type(virq + i, IRQ_TYPE_EDGE_RISING); if (ret) goto error; + + ret = gic_shared_irq_domain_map(d, virq + i, hwirq, cpu); + if (ret) + goto error; + + i++; } return 0; error: - irq_domain_free_irqs_parent(d, virq, nr_irqs); + bitmap_set(ipi_available, base_hwirq, nr_irqs); return ret; } void gic_ipi_domain_free(struct irq_domain *d, unsigned int virq, unsigned int nr_irqs) { - irq_domain_free_irqs_parent(d, virq, nr_irqs); + irq_hw_number_t base_hwirq; + struct irq_data *data; + + data = irq_get_irq_data(virq); + if (!data) + return; + + base_hwirq = GIC_HWIRQ_TO_SHARED(irqd_to_hwirq(data)); + bitmap_set(ipi_available, base_hwirq, nr_irqs); } int gic_ipi_domain_match(struct irq_domain *d, struct device_node *node, @@ -1072,13 +975,6 @@ static void __init __gic_init(unsigned long gic_base_addr, panic("Failed to add GIC IRQ domain"); gic_irq_domain->name = "mips-gic-irq"; - gic_dev_domain = irq_domain_add_hierarchy(gic_irq_domain, 0, - GIC_NUM_LOCAL_INTRS + gic_shared_intrs, - node, &gic_dev_domain_ops, NULL); - if (!gic_dev_domain) - panic("Failed to add GIC DEV domain"); - gic_dev_domain->name = "mips-gic-dev"; - gic_ipi_domain = irq_domain_add_hierarchy(gic_irq_domain, IRQ_DOMAIN_FLAG_IPI_PER_CPU, GIC_NUM_LOCAL_INTRS + gic_shared_intrs, |