Add a new ipi domain on top of the normal domain. The only reason this domain needs to be hierarchal is because we need to use the alloc function to allocate the IPI. Should we make the gic IPI a completely different irqchip? Signed-off-by: Qais Yousef <qais.yousef@xxxxxxxxxx> --- drivers/irqchip/Kconfig | 1 + drivers/irqchip/irq-mips-gic.c | 101 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 97 insertions(+), 5 deletions(-) diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 27b52c8729cd..e07593f4860d 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -167,6 +167,7 @@ config KEYSTONE_IRQ config MIPS_GIC bool + select IRQ_DOMAIN_HIERARCHY select MIPS_CM config INGENIC_IRQ diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index af2f16bb8a94..14e99ea0f963 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c @@ -33,6 +33,7 @@ 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_ipi_domain; static int gic_shared_intrs; static int gic_vpes; static unsigned int gic_cpu_pin; @@ -733,7 +734,7 @@ static int gic_local_irq_domain_map(struct irq_domain *d, unsigned int virq, } static int gic_shared_irq_domain_map(struct irq_domain *d, unsigned int virq, - irq_hw_number_t hw) + irq_hw_number_t hw, unsigned int vpe) { int intr = GIC_HWIRQ_TO_SHARED(hw); unsigned long flags; @@ -743,9 +744,8 @@ static int gic_shared_irq_domain_map(struct irq_domain *d, unsigned int virq, spin_lock_irqsave(&gic_lock, flags); gic_map_to_pin(intr, gic_cpu_pin); - /* Map to VPE 0 by default */ - gic_map_to_vpe(intr, 0); - set_bit(intr, pcpu_masks[0].pcpu_mask); + gic_map_to_vpe(intr, vpe); + set_bit(intr, pcpu_masks[vpe].pcpu_mask); spin_unlock_irqrestore(&gic_lock, flags); return 0; @@ -756,7 +756,7 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq, { if (GIC_HWIRQ_TO_LOCAL(hw) < GIC_NUM_LOCAL_INTRS) return gic_local_irq_domain_map(d, virq, hw); - return gic_shared_irq_domain_map(d, virq, hw); + return gic_shared_irq_domain_map(d, virq, hw, 0); } static int gic_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr, @@ -783,6 +783,91 @@ static const struct irq_domain_ops gic_irq_domain_ops = { .xlate = gic_irq_domain_xlate, }; +static int gic_ipi_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) +{ + /* + * There's nothing to translate here. hwirq is dynamically allocated and + * the irq type is always edge triggered. + * */ + *out_hwirq = 0; + *out_type = IRQ_TYPE_EDGE_RISING; + + return 0; +} + +static int gic_ipi_domain_alloc(struct irq_domain *d, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + int vpe, ret; + struct ipi_virq *v = arg; + struct ipi_hwirq *h; + irq_hw_number_t hwirq; + + for_each_cpu(vpe, &v->cpumask) { + /* we need a GIC shared hwirq for each vpe */ + h = irq_domain_get_ipi_hwirq(d); + if (!h) { + ret = -EINVAL; + goto error; + } + cpumask_set_cpu(vpe, &h->cpumask); + + /* add this hwirq to the virq mapping list */ + ret = irq_domain_ipi_virq_add_hwirq(v, h); + if (ret) + goto error; + + hwirq = GIC_SHARED_TO_HWIRQ(h->hwirq); + ret = irq_domain_set_hwirq_and_chip(d, virq + vpe, hwirq, + NULL, NULL); + if (ret) + goto error; + + ret = gic_shared_irq_domain_map(d, virq + vpe, hwirq, vpe); + if (ret) + goto error; + } + + return 0; +error: + while ((h = irq_domain_ipi_virq_rm_hwirq(v))) { + for_each_cpu(vpe, &h->cpumask) + clear_bit(h->hwirq, pcpu_masks[vpe].pcpu_mask); + irq_domain_put_ipi_hwirq(d, h); + } + + return ret; +} + +void gic_ipi_domain_free(struct irq_domain *d, unsigned int virq, + unsigned int nr_irqs) +{ + struct ipi_hwirq *h; + struct ipi_virq *v; + int vpe; + + v = irq_domain_find_ipi_virq(d, virq); + if (!v) + return; + + /* return all allocated hwirqs to the IPI domain */ + while ((h = irq_domain_ipi_virq_rm_hwirq(v))) { + for_each_cpu(vpe, &h->cpumask) + clear_bit(h->hwirq, pcpu_masks[vpe].pcpu_mask); + irq_domain_put_ipi_hwirq(d, h); + } +} + +static struct irq_domain_ops gic_ipi_domain_ops = { + .xlate = gic_ipi_domain_xlate, + .alloc = gic_ipi_domain_alloc, + .free = gic_ipi_domain_free, + .send_ipi = gic_send_ipi, +}; + static void __init __gic_init(unsigned long gic_base_addr, unsigned long gic_addrspace_size, unsigned int cpu_vec, unsigned int irqbase, @@ -842,6 +927,12 @@ static void __init __gic_init(unsigned long gic_base_addr, if (!gic_irq_domain) panic("Failed to add GIC IRQ domain"); + gic_ipi_domain = irq_domain_add_hierarchy(gic_irq_domain, IRQ_DOMAIN_FLAG_IPI, + GIC_NUM_LOCAL_INTRS + gic_shared_intrs, + NULL, &gic_ipi_domain_ops, NULL); + if (!gic_ipi_domain) + panic("Failed to add GIC IPI domain"); + gic_basic_init(); gic_ipi_init(); -- 2.1.0