Commit b87281e7 authored by Paul Burton's avatar Paul Burton Committed by Thomas Gleixner

irqchip/mips-gic: Remove device IRQ domain

In commit c98c1822 ("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: default avatarPaul Burton <paul.burton@imgtec.com>
Signed-off-by: default avatarMatt 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.comSigned-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent f8dcd9e8
...@@ -29,25 +29,12 @@ struct gic_pcpu_mask { ...@@ -29,25 +29,12 @@ struct gic_pcpu_mask {
DECLARE_BITMAP(pcpu_mask, GIC_MAX_INTRS); 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 unsigned long __gic_base_addr;
static void __iomem *gic_base; static void __iomem *gic_base;
static struct gic_pcpu_mask pcpu_masks[NR_CPUS]; static struct gic_pcpu_mask pcpu_masks[NR_CPUS];
static DEFINE_SPINLOCK(gic_lock); static DEFINE_SPINLOCK(gic_lock);
static struct irq_domain *gic_irq_domain; static struct irq_domain *gic_irq_domain;
static struct irq_domain *gic_dev_domain;
static struct irq_domain *gic_ipi_domain; static struct irq_domain *gic_ipi_domain;
static int gic_shared_intrs; static int gic_shared_intrs;
static int gic_vpes; static int gic_vpes;
...@@ -694,132 +681,7 @@ static int gic_shared_irq_domain_map(struct irq_domain *d, unsigned int virq, ...@@ -694,132 +681,7 @@ static int gic_shared_irq_domain_map(struct irq_domain *d, unsigned int virq,
return 0; return 0;
} }
static int gic_setup_dev_chip(struct irq_domain *d, unsigned int virq, static int gic_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
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,
const u32 *intspec, unsigned int intsize, const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, irq_hw_number_t *out_hwirq,
unsigned int *out_type) unsigned int *out_type)
...@@ -838,58 +700,73 @@ static int gic_dev_domain_xlate(struct irq_domain *d, struct device_node *ctrlr, ...@@ -838,58 +700,73 @@ static int gic_dev_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
return 0; 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) unsigned int nr_irqs, void *arg)
{ {
struct irq_fwspec *fwspec = arg; struct irq_fwspec *fwspec = arg;
struct gic_irq_spec spec = { irq_hw_number_t hwirq;
.type = GIC_DEVICE, int err;
};
int i, ret;
if (fwspec->param[0] == GIC_SHARED) if (fwspec->param[0] == GIC_SHARED) {
spec.hwirq = GIC_SHARED_TO_HWIRQ(fwspec->param[1]); hwirq = GIC_SHARED_TO_HWIRQ(fwspec->param[1]);
else
spec.hwirq = GIC_LOCAL_TO_HWIRQ(fwspec->param[1]);
ret = irq_domain_alloc_irqs_parent(d, virq, nr_irqs, &spec); /* verify that shared irqs don't conflict with an IPI irq */
if (ret) if (test_bit(GIC_HWIRQ_TO_SHARED(hwirq), ipi_resrv))
return ret; return -EBUSY;
for (i = 0; i < nr_irqs; i++) { err = irq_domain_set_hwirq_and_chip(d, virq, hwirq,
ret = gic_setup_dev_chip(d, virq + i, spec.hwirq + i); &gic_level_irq_controller,
if (ret) NULL);
goto error; 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: switch (GIC_HWIRQ_TO_LOCAL(hwirq)) {
irq_domain_free_irqs_parent(d, virq, nr_irqs); case GIC_LOCAL_INT_TIMER:
return ret; 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, irq_set_handler(virq, handle_percpu_irq);
unsigned int nr_irqs) break;
{
/* no real allocation is done for dev irqs, so no need to free anything */ default:
return; 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, void gic_irq_domain_free(struct irq_domain *d, unsigned int virq,
struct irq_data *d) 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 = { static const struct irq_domain_ops gic_irq_domain_ops = {
.xlate = gic_dev_domain_xlate, .xlate = gic_irq_domain_xlate,
.alloc = gic_dev_domain_alloc, .alloc = gic_irq_domain_alloc,
.free = gic_dev_domain_free, .free = gic_irq_domain_free,
.activate = gic_dev_domain_activate,
}; };
static int gic_ipi_domain_xlate(struct irq_domain *d, struct device_node *ctrlr, 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, ...@@ -911,20 +788,32 @@ static int gic_ipi_domain_alloc(struct irq_domain *d, unsigned int virq,
unsigned int nr_irqs, void *arg) unsigned int nr_irqs, void *arg)
{ {
struct cpumask *ipimask = arg; struct cpumask *ipimask = arg;
struct gic_irq_spec spec = { irq_hw_number_t hwirq, base_hwirq;
.type = GIC_IPI, int cpu, ret, i;
.ipimask = ipimask
}; base_hwirq = find_first_bit(ipi_available, gic_shared_intrs);
int ret, i; if (base_hwirq == gic_shared_intrs)
return -ENOMEM;
ret = irq_domain_alloc_irqs_parent(d, virq, nr_irqs, &spec); /* check that we have enough space */
if (ret) for (i = base_hwirq; i < nr_irqs; i++) {
return ret; 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 */ ret = irq_domain_set_hwirq_and_chip(d->parent, virq + i, hwirq,
for (i = 0; i < nr_irqs; i++) {
ret = irq_domain_set_hwirq_and_chip(d, virq + i,
GIC_SHARED_TO_HWIRQ(spec.hwirq + i),
&gic_edge_irq_controller, &gic_edge_irq_controller,
NULL); NULL);
if (ret) if (ret)
...@@ -933,18 +822,32 @@ static int gic_ipi_domain_alloc(struct irq_domain *d, unsigned int virq, ...@@ -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); ret = irq_set_irq_type(virq + i, IRQ_TYPE_EDGE_RISING);
if (ret) if (ret)
goto error; goto error;
ret = gic_shared_irq_domain_map(d, virq + i, hwirq, cpu);
if (ret)
goto error;
i++;
} }
return 0; return 0;
error: error:
irq_domain_free_irqs_parent(d, virq, nr_irqs); bitmap_set(ipi_available, base_hwirq, nr_irqs);
return ret; return ret;
} }
void gic_ipi_domain_free(struct irq_domain *d, unsigned int virq, void gic_ipi_domain_free(struct irq_domain *d, unsigned int virq,
unsigned int nr_irqs) 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, 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, ...@@ -1072,13 +975,6 @@ static void __init __gic_init(unsigned long gic_base_addr,
panic("Failed to add GIC IRQ domain"); panic("Failed to add GIC IRQ domain");
gic_irq_domain->name = "mips-gic-irq"; 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, gic_ipi_domain = irq_domain_add_hierarchy(gic_irq_domain,
IRQ_DOMAIN_FLAG_IPI_PER_CPU, IRQ_DOMAIN_FLAG_IPI_PER_CPU,
GIC_NUM_LOCAL_INTRS + gic_shared_intrs, GIC_NUM_LOCAL_INTRS + gic_shared_intrs,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment