Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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.com


Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent f8dcd9e8
Loading
Loading
Loading
Loading
+93 −197
Original line number Diff line number Diff line
@@ -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,17 +681,50 @@ 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)
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)
{
	struct irq_chip *chip;
	if (intsize != 3)
		return -EINVAL;

	if (intspec[0] == GIC_SHARED)
		*out_hwirq = GIC_SHARED_TO_HWIRQ(intspec[1]);
	else if (intspec[0] == GIC_LOCAL)
		*out_hwirq = GIC_LOCAL_TO_HWIRQ(intspec[1]);
	else
		return -EINVAL;
	*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;

	return 0;
}

static int gic_irq_domain_alloc(struct irq_domain *d, unsigned int virq,
				unsigned int nr_irqs, void *arg)
{
	struct irq_fwspec *fwspec = arg;
	irq_hw_number_t hwirq;
	int err;

	if (hwirq >= GIC_SHARED_HWIRQ_BASE) {
	if (fwspec->param[0] == GIC_SHARED) {
		hwirq = GIC_SHARED_TO_HWIRQ(fwspec->param[1]);

		/* verify that shared irqs don't conflict with an IPI irq */
		if (test_bit(GIC_HWIRQ_TO_SHARED(hwirq), ipi_resrv))
			return -EBUSY;

		err = irq_domain_set_hwirq_and_chip(d, virq, hwirq,
						    &gic_level_irq_controller,
						    NULL);
	} else {
		if (err)
			return err;

		return gic_shared_irq_domain_map(d, virq, hwirq, 0);
	}

	hwirq = GIC_LOCAL_TO_HWIRQ(fwspec->param[1]);

	switch (GIC_HWIRQ_TO_LOCAL(hwirq)) {
	case GIC_LOCAL_INT_TIMER:
	case GIC_LOCAL_INT_PERFCTR:
@@ -714,43 +734,66 @@ static int gic_setup_dev_chip(struct irq_domain *d, unsigned int virq,
		 * the rest of the MIPS kernel code does not use the
		 * percpu IRQ API for them.
		 */
			chip = &gic_all_vpes_local_irq_controller;
		err = irq_domain_set_hwirq_and_chip(d, virq, hwirq,
						    &gic_all_vpes_local_irq_controller,
						    NULL);
		if (err)
			return err;

		irq_set_handler(virq, handle_percpu_irq);
		break;

	default:
			chip = &gic_local_irq_controller;
		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;
	}

		err = irq_domain_set_hwirq_and_chip(d, virq, hwirq,
						    chip, NULL);
	return gic_local_irq_domain_map(d, virq, hwirq);
}

	return err;
void gic_irq_domain_free(struct irq_domain *d, unsigned int virq,
			 unsigned int nr_irqs)
{
}

static int gic_irq_domain_alloc(struct irq_domain *d, unsigned int virq,
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,
				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)
{
	struct gic_irq_spec *spec = arg;
	struct cpumask *ipimask = 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) {
	if (base_hwirq == gic_shared_intrs)
		return -ENOMEM;
		}

	/* check that we have enough space */
	for (i = base_hwirq; i < nr_irqs; i++) {
@@ -761,16 +804,24 @@ static int gic_irq_domain_alloc(struct irq_domain *d, unsigned int virq,

	/* map the hwirq for each cpu consecutively */
	i = 0;
		for_each_cpu(cpu, spec->ipimask) {
	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_level_irq_controller,
						    &gic_edge_irq_controller,
						    NULL);
		if (ret)
			goto error;

		ret = irq_domain_set_hwirq_and_chip(d->parent, virq + i, hwirq,
						    &gic_edge_irq_controller,
						    NULL);
		if (ret)
			goto error;

			irq_set_handler(virq + i, handle_level_irq);
		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)
@@ -779,20 +830,13 @@ static int gic_irq_domain_alloc(struct irq_domain *d, unsigned int virq,
		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,
void gic_ipi_domain_free(struct irq_domain *d, unsigned int virq,
			 unsigned int nr_irqs)
{
	irq_hw_number_t base_hwirq;
@@ -806,147 +850,6 @@ void gic_irq_domain_free(struct irq_domain *d, unsigned int virq,
	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,
				irq_hw_number_t *out_hwirq,
				unsigned int *out_type)
{
	if (intsize != 3)
		return -EINVAL;

	if (intspec[0] == GIC_SHARED)
		*out_hwirq = GIC_SHARED_TO_HWIRQ(intspec[1]);
	else if (intspec[0] == GIC_LOCAL)
		*out_hwirq = GIC_LOCAL_TO_HWIRQ(intspec[1]);
	else
		return -EINVAL;
	*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;

	return 0;
}

static int gic_dev_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;

	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]);

	ret = irq_domain_alloc_irqs_parent(d, virq, nr_irqs, &spec);
	if (ret)
		return ret;

	for (i = 0; i < nr_irqs; i++) {
		ret = gic_setup_dev_chip(d, virq + i, spec.hwirq + i);
		if (ret)
			goto error;
	}

	return 0;

error:
	irq_domain_free_irqs_parent(d, virq, nr_irqs);
	return ret;
}

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;
}

static void gic_dev_domain_activate(struct irq_domain *domain,
				    struct irq_data *d)
{
	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 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)
{
	struct cpumask *ipimask = arg;
	struct gic_irq_spec spec = {
		.type = GIC_IPI,
		.ipimask = ipimask
	};
	int ret, i;

	ret = irq_domain_alloc_irqs_parent(d, virq, nr_irqs, &spec);
	if (ret)
		return ret;

	/* 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),
						    &gic_edge_irq_controller,
						    NULL);
		if (ret)
			goto error;

		ret = irq_set_irq_type(virq + i, IRQ_TYPE_EDGE_RISING);
		if (ret)
			goto error;
	}

	return 0;
error:
	irq_domain_free_irqs_parent(d, virq, 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);
}

int gic_ipi_domain_match(struct irq_domain *d, struct device_node *node,
			 enum irq_domain_bus_token bus_token)
{
@@ -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,