Revision 8f4fd86aa5d6aa122619623910065d236592e37c authored by David Woodhouse on 06 January 2021, 15:39:55 UTC, committed by Juergen Gross on 13 January 2021, 15:12:03 UTC
With INTX or GSI delivery, Xen uses the event channel structures of CPU0.

If the interrupt gets handled by Linux on a different CPU, then no events
are seen as pending. Rather than introducing locking to allow other CPUs
to process CPU0's events, just ensure that the PCI interrupts happens
only on CPU0.

Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Reviewed-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Link: https://lore.kernel.org/r/20210106153958.584169-3-dwmw2@infradead.org
Signed-off-by: Juergen Gross <jgross@suse.com>
1 parent 3499ba8
Raw File
irq-davinci-aintc.c
// SPDX-License-Identifier: GPL-2.0-or-later
//
// Copyright (C) 2006, 2019 Texas Instruments.
//
// Interrupt handler for DaVinci boards.

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqchip/irq-davinci-aintc.h>
#include <linux/io.h>
#include <linux/irqdomain.h>

#include <asm/exception.h>

#define DAVINCI_AINTC_FIQ_REG0		0x00
#define DAVINCI_AINTC_FIQ_REG1		0x04
#define DAVINCI_AINTC_IRQ_REG0		0x08
#define DAVINCI_AINTC_IRQ_REG1		0x0c
#define DAVINCI_AINTC_IRQ_IRQENTRY	0x14
#define DAVINCI_AINTC_IRQ_ENT_REG0	0x18
#define DAVINCI_AINTC_IRQ_ENT_REG1	0x1c
#define DAVINCI_AINTC_IRQ_INCTL_REG	0x20
#define DAVINCI_AINTC_IRQ_EABASE_REG	0x24
#define DAVINCI_AINTC_IRQ_INTPRI0_REG	0x30
#define DAVINCI_AINTC_IRQ_INTPRI7_REG	0x4c

static void __iomem *davinci_aintc_base;
static struct irq_domain *davinci_aintc_irq_domain;

static inline void davinci_aintc_writel(unsigned long value, int offset)
{
	writel_relaxed(value, davinci_aintc_base + offset);
}

static inline unsigned long davinci_aintc_readl(int offset)
{
	return readl_relaxed(davinci_aintc_base + offset);
}

static __init void
davinci_aintc_setup_gc(void __iomem *base,
		       unsigned int irq_start, unsigned int num)
{
	struct irq_chip_generic *gc;
	struct irq_chip_type *ct;

	gc = irq_get_domain_generic_chip(davinci_aintc_irq_domain, irq_start);
	gc->reg_base = base;
	gc->irq_base = irq_start;

	ct = gc->chip_types;
	ct->chip.irq_ack = irq_gc_ack_set_bit;
	ct->chip.irq_mask = irq_gc_mask_clr_bit;
	ct->chip.irq_unmask = irq_gc_mask_set_bit;

	ct->regs.ack = DAVINCI_AINTC_IRQ_REG0;
	ct->regs.mask = DAVINCI_AINTC_IRQ_ENT_REG0;
	irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE,
			       IRQ_NOREQUEST | IRQ_NOPROBE, 0);
}

static asmlinkage void __exception_irq_entry
davinci_aintc_handle_irq(struct pt_regs *regs)
{
	int irqnr = davinci_aintc_readl(DAVINCI_AINTC_IRQ_IRQENTRY);

	/*
	 * Use the formula for entry vector index generation from section
	 * 8.3.3 of the manual.
	 */
	irqnr >>= 2;
	irqnr -= 1;

	handle_domain_irq(davinci_aintc_irq_domain, irqnr, regs);
}

/* ARM Interrupt Controller Initialization */
void __init davinci_aintc_init(const struct davinci_aintc_config *config)
{
	unsigned int irq_off, reg_off, prio, shift;
	void __iomem *req;
	int ret, irq_base;
	const u8 *prios;

	req = request_mem_region(config->reg.start,
				 resource_size(&config->reg),
				 "davinci-cp-intc");
	if (!req) {
		pr_err("%s: register range busy\n", __func__);
		return;
	}

	davinci_aintc_base = ioremap(config->reg.start,
				     resource_size(&config->reg));
	if (!davinci_aintc_base) {
		pr_err("%s: unable to ioremap register range\n", __func__);
		return;
	}

	/* Clear all interrupt requests */
	davinci_aintc_writel(~0x0, DAVINCI_AINTC_FIQ_REG0);
	davinci_aintc_writel(~0x0, DAVINCI_AINTC_FIQ_REG1);
	davinci_aintc_writel(~0x0, DAVINCI_AINTC_IRQ_REG0);
	davinci_aintc_writel(~0x0, DAVINCI_AINTC_IRQ_REG1);

	/* Disable all interrupts */
	davinci_aintc_writel(0x0, DAVINCI_AINTC_IRQ_ENT_REG0);
	davinci_aintc_writel(0x0, DAVINCI_AINTC_IRQ_ENT_REG1);

	/* Interrupts disabled immediately, IRQ entry reflects all */
	davinci_aintc_writel(0x0, DAVINCI_AINTC_IRQ_INCTL_REG);

	/* we don't use the hardware vector table, just its entry addresses */
	davinci_aintc_writel(0, DAVINCI_AINTC_IRQ_EABASE_REG);

	/* Clear all interrupt requests */
	davinci_aintc_writel(~0x0, DAVINCI_AINTC_FIQ_REG0);
	davinci_aintc_writel(~0x0, DAVINCI_AINTC_FIQ_REG1);
	davinci_aintc_writel(~0x0, DAVINCI_AINTC_IRQ_REG0);
	davinci_aintc_writel(~0x0, DAVINCI_AINTC_IRQ_REG1);

	prios = config->prios;
	for (reg_off = DAVINCI_AINTC_IRQ_INTPRI0_REG;
	     reg_off <= DAVINCI_AINTC_IRQ_INTPRI7_REG; reg_off += 4) {
		for (shift = 0, prio = 0; shift < 32; shift += 4, prios++)
			prio |= (*prios & 0x07) << shift;
		davinci_aintc_writel(prio, reg_off);
	}

	irq_base = irq_alloc_descs(-1, 0, config->num_irqs, 0);
	if (irq_base < 0) {
		pr_err("%s: unable to allocate interrupt descriptors: %d\n",
		       __func__, irq_base);
		return;
	}

	davinci_aintc_irq_domain = irq_domain_add_legacy(NULL,
						config->num_irqs, irq_base, 0,
						&irq_domain_simple_ops, NULL);
	if (!davinci_aintc_irq_domain) {
		pr_err("%s: unable to create interrupt domain\n", __func__);
		return;
	}

	ret = irq_alloc_domain_generic_chips(davinci_aintc_irq_domain, 32, 1,
					     "AINTC", handle_edge_irq,
					     IRQ_NOREQUEST | IRQ_NOPROBE, 0, 0);
	if (ret) {
		pr_err("%s: unable to allocate generic irq chips for domain\n",
		       __func__);
		return;
	}

	for (irq_off = 0, reg_off = 0;
	     irq_off < config->num_irqs;
	     irq_off += 32, reg_off += 0x04)
		davinci_aintc_setup_gc(davinci_aintc_base + reg_off,
				       irq_base + irq_off, 32);

	set_handle_irq(davinci_aintc_handle_irq);
}
back to top