Commit 0809ae72 authored by Marc Zyngier's avatar Marc Zyngier

irqchip/bcm2836: Configure mailbox interrupts as standard interrupts

In order to switch the bcm2836 driver to privide standard interrupts
for IPIs, it first needs to stop lying about the way things work.

The mailbox interrupt is actually a multiplexer, with enough
bits to store 32 pending interrupts per CPU. So let's turn it
into a chained irqchip.

Once this is done, we can instanciate the corresponding IPIs,
and pass them to the architecture code.
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parent 3567c6ca
......@@ -10,6 +10,7 @@
#include <linux/of_irq.h>
#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/irq-bcm2836.h>
#include <asm/exception.h>
......@@ -89,12 +90,24 @@ static struct irq_chip bcm2836_arm_irqchip_gpu = {
.irq_unmask = bcm2836_arm_irqchip_unmask_gpu_irq,
};
static void bcm2836_arm_irqchip_dummy_op(struct irq_data *d)
{
}
static struct irq_chip bcm2836_arm_irqchip_dummy = {
.name = "bcm2836-dummy",
.irq_eoi = bcm2836_arm_irqchip_dummy_op,
};
static int bcm2836_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
struct irq_chip *chip;
switch (hw) {
case LOCAL_IRQ_MAILBOX0:
chip = &bcm2836_arm_irqchip_dummy;
break;
case LOCAL_IRQ_CNTPSIRQ:
case LOCAL_IRQ_CNTPNSIRQ:
case LOCAL_IRQ_CNTHPIRQ:
......@@ -127,17 +140,7 @@ __exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs)
u32 stat;
stat = readl_relaxed(intc.base + LOCAL_IRQ_PENDING0 + 4 * cpu);
if (stat & BIT(LOCAL_IRQ_MAILBOX0)) {
#ifdef CONFIG_SMP
void __iomem *mailbox0 = (intc.base +
LOCAL_MAILBOX0_CLR0 + 16 * cpu);
u32 mbox_val = readl(mailbox0);
u32 ipi = ffs(mbox_val) - 1;
writel(1 << ipi, mailbox0);
handle_IPI(ipi, regs);
#endif
} else if (stat) {
if (stat) {
u32 hwirq = ffs(stat) - 1;
handle_domain_irq(intc.domain, hwirq, regs);
......@@ -145,8 +148,35 @@ __exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs)
}
#ifdef CONFIG_SMP
static void bcm2836_arm_irqchip_send_ipi(const struct cpumask *mask,
unsigned int ipi)
static struct irq_domain *ipi_domain;
static void bcm2836_arm_irqchip_handle_ipi(struct irq_desc *desc)
{
struct irq_chip *chip = irq_desc_get_chip(desc);
int cpu = smp_processor_id();
u32 mbox_val;
chained_irq_enter(chip, desc);
mbox_val = readl_relaxed(intc.base + LOCAL_MAILBOX0_CLR0 + 16 * cpu);
if (mbox_val) {
int hwirq = ffs(mbox_val) - 1;
generic_handle_irq(irq_find_mapping(ipi_domain, hwirq));
}
chained_irq_exit(chip, desc);
}
static void bcm2836_arm_irqchip_ipi_eoi(struct irq_data *d)
{
int cpu = smp_processor_id();
writel_relaxed(BIT(d->hwirq),
intc.base + LOCAL_MAILBOX0_CLR0 + 16 * cpu);
}
static void bcm2836_arm_irqchip_ipi_send_mask(struct irq_data *d,
const struct cpumask *mask)
{
int cpu;
void __iomem *mailbox0_base = intc.base + LOCAL_MAILBOX0_SET0;
......@@ -157,11 +187,45 @@ static void bcm2836_arm_irqchip_send_ipi(const struct cpumask *mask,
*/
smp_wmb();
for_each_cpu(cpu, mask) {
writel(1 << ipi, mailbox0_base + 16 * cpu);
for_each_cpu(cpu, mask)
writel_relaxed(BIT(d->hwirq), mailbox0_base + 16 * cpu);
}
static struct irq_chip bcm2836_arm_irqchip_ipi = {
.name = "IPI",
.irq_eoi = bcm2836_arm_irqchip_ipi_eoi,
.ipi_send_mask = bcm2836_arm_irqchip_ipi_send_mask,
};
static int bcm2836_arm_irqchip_ipi_alloc(struct irq_domain *d,
unsigned int virq,
unsigned int nr_irqs, void *args)
{
int i;
for (i = 0; i < nr_irqs; i++) {
irq_set_percpu_devid(virq + i);
irq_domain_set_info(d, virq + i, i, &bcm2836_arm_irqchip_ipi,
d->host_data,
handle_percpu_devid_fasteoi_ipi,
NULL, NULL);
}
return 0;
}
static void bcm2836_arm_irqchip_ipi_free(struct irq_domain *d,
unsigned int virq,
unsigned int nr_irqs)
{
/* Not freeing IPIs */
}
static const struct irq_domain_ops ipi_domain_ops = {
.alloc = bcm2836_arm_irqchip_ipi_alloc,
.free = bcm2836_arm_irqchip_ipi_free,
};
static int bcm2836_cpu_starting(unsigned int cpu)
{
bcm2836_arm_irqchip_unmask_per_cpu_irq(LOCAL_MAILBOX_INT_CONTROL0, 0,
......@@ -175,25 +239,58 @@ static int bcm2836_cpu_dying(unsigned int cpu)
cpu);
return 0;
}
#endif
static const struct irq_domain_ops bcm2836_arm_irqchip_intc_ops = {
.xlate = irq_domain_xlate_onetwocell,
.map = bcm2836_map,
};
#define BITS_PER_MBOX 32
static void
bcm2836_arm_irqchip_smp_init(void)
static void bcm2836_arm_irqchip_smp_init(void)
{
#ifdef CONFIG_SMP
struct irq_fwspec ipi_fwspec = {
.fwnode = intc.domain->fwnode,
.param_count = 1,
.param = {
[0] = LOCAL_IRQ_MAILBOX0,
},
};
int base_ipi, mux_irq;
mux_irq = irq_create_fwspec_mapping(&ipi_fwspec);
if (WARN_ON(mux_irq <= 0))
return;
ipi_domain = irq_domain_create_linear(intc.domain->fwnode,
BITS_PER_MBOX, &ipi_domain_ops,
NULL);
if (WARN_ON(!ipi_domain))
return;
ipi_domain->flags |= IRQ_DOMAIN_FLAG_IPI_SINGLE;
irq_domain_update_bus_token(ipi_domain, DOMAIN_BUS_IPI);
base_ipi = __irq_domain_alloc_irqs(ipi_domain, -1, BITS_PER_MBOX,
NUMA_NO_NODE, NULL,
false, NULL);
if (WARN_ON(!base_ipi))
return;
set_smp_ipi_range(base_ipi, BITS_PER_MBOX);
irq_set_chained_handler_and_data(mux_irq,
bcm2836_arm_irqchip_handle_ipi, NULL);
/* Unmask IPIs to the boot CPU. */
cpuhp_setup_state(CPUHP_AP_IRQ_BCM2836_STARTING,
"irqchip/bcm2836:starting", bcm2836_cpu_starting,
bcm2836_cpu_dying);
set_smp_cross_call(bcm2836_arm_irqchip_send_ipi);
#endif
}
#else
#define bcm2836_arm_irqchip_smp_init() do { } while(0)
#endif
static const struct irq_domain_ops bcm2836_arm_irqchip_intc_ops = {
.xlate = irq_domain_xlate_onetwocell,
.map = bcm2836_map,
};
/*
* The LOCAL_IRQ_CNT* timer firings are based off of the external
......@@ -232,6 +329,8 @@ static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node,
if (!intc.domain)
panic("%pOF: unable to create IRQ domain\n", node);
irq_domain_update_bus_token(intc.domain, DOMAIN_BUS_WIRED);
bcm2836_arm_irqchip_smp_init();
set_handle_irq(bcm2836_arm_irqchip_handle_irq);
......
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