irqchip/hip04: Configure IPIs as standard interrupts

In order to switch the hip04 driver to provide standard interrupts
for IPIs, rework the way interrupts are allocated, making sure
the irqdomain covers the SGIs as well as the rest of the interrupt
range.

The driver is otherwise so old-school that it creates all interrupts
upfront (duh!), so there is hardly anything else to change, apart
from communicating the IPIs to the arch code.

Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
Marc Zyngier 2020-06-20 20:02:18 +01:00
parent 0809ae7249
commit a2df12c589

View File

@ -171,6 +171,29 @@ static int hip04_irq_set_affinity(struct irq_data *d,
return IRQ_SET_MASK_OK; return IRQ_SET_MASK_OK;
} }
static void hip04_ipi_send_mask(struct irq_data *d, const struct cpumask *mask)
{
int cpu;
unsigned long flags, map = 0;
raw_spin_lock_irqsave(&irq_controller_lock, flags);
/* Convert our logical CPU mask into a physical one. */
for_each_cpu(cpu, mask)
map |= hip04_cpu_map[cpu];
/*
* Ensure that stores to Normal memory are visible to the
* other CPUs before they observe us issuing the IPI.
*/
dmb(ishst);
/* this always happens on GIC0 */
writel_relaxed(map << 8 | d->hwirq, hip04_data.dist_base + GIC_DIST_SOFTINT);
raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
}
#endif #endif
static void __exception_irq_entry hip04_handle_irq(struct pt_regs *regs) static void __exception_irq_entry hip04_handle_irq(struct pt_regs *regs)
@ -182,19 +205,9 @@ static void __exception_irq_entry hip04_handle_irq(struct pt_regs *regs)
irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK); irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
irqnr = irqstat & GICC_IAR_INT_ID_MASK; irqnr = irqstat & GICC_IAR_INT_ID_MASK;
if (likely(irqnr > 15 && irqnr <= HIP04_MAX_IRQS)) { if (irqnr <= HIP04_MAX_IRQS)
handle_domain_irq(hip04_data.domain, irqnr, regs); handle_domain_irq(hip04_data.domain, irqnr, regs);
continue; } while (irqnr > HIP04_MAX_IRQS);
}
if (irqnr < 16) {
writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
#ifdef CONFIG_SMP
handle_IPI(irqnr, regs);
#endif
continue;
}
break;
} while (1);
} }
static struct irq_chip hip04_irq_chip = { static struct irq_chip hip04_irq_chip = {
@ -205,6 +218,7 @@ static struct irq_chip hip04_irq_chip = {
.irq_set_type = hip04_irq_set_type, .irq_set_type = hip04_irq_set_type,
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
.irq_set_affinity = hip04_irq_set_affinity, .irq_set_affinity = hip04_irq_set_affinity,
.ipi_send_mask = hip04_ipi_send_mask,
#endif #endif
.flags = IRQCHIP_SET_TYPE_MASKED | .flags = IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_SKIP_SET_WAKE | IRQCHIP_SKIP_SET_WAKE |
@ -279,39 +293,17 @@ static void hip04_irq_cpu_init(struct hip04_irq_data *intc)
writel_relaxed(1, base + GIC_CPU_CTRL); writel_relaxed(1, base + GIC_CPU_CTRL);
} }
#ifdef CONFIG_SMP
static void hip04_raise_softirq(const struct cpumask *mask, unsigned int irq)
{
int cpu;
unsigned long flags, map = 0;
raw_spin_lock_irqsave(&irq_controller_lock, flags);
/* Convert our logical CPU mask into a physical one. */
for_each_cpu(cpu, mask)
map |= hip04_cpu_map[cpu];
/*
* Ensure that stores to Normal memory are visible to the
* other CPUs before they observe us issuing the IPI.
*/
dmb(ishst);
/* this always happens on GIC0 */
writel_relaxed(map << 8 | irq, hip04_data.dist_base + GIC_DIST_SOFTINT);
raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
}
#endif
static int hip04_irq_domain_map(struct irq_domain *d, unsigned int irq, static int hip04_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw) irq_hw_number_t hw)
{ {
if (hw < 32) { if (hw < 16) {
irq_set_percpu_devid(irq);
irq_set_chip_and_handler(irq, &hip04_irq_chip,
handle_percpu_devid_fasteoi_ipi);
} else if (hw < 32) {
irq_set_percpu_devid(irq); irq_set_percpu_devid(irq);
irq_set_chip_and_handler(irq, &hip04_irq_chip, irq_set_chip_and_handler(irq, &hip04_irq_chip,
handle_percpu_devid_irq); handle_percpu_devid_irq);
irq_set_status_flags(irq, IRQ_NOAUTOEN);
} else { } else {
irq_set_chip_and_handler(irq, &hip04_irq_chip, irq_set_chip_and_handler(irq, &hip04_irq_chip,
handle_fasteoi_irq); handle_fasteoi_irq);
@ -328,10 +320,13 @@ static int hip04_irq_domain_xlate(struct irq_domain *d,
unsigned long *out_hwirq, unsigned long *out_hwirq,
unsigned int *out_type) unsigned int *out_type)
{ {
unsigned long ret = 0;
if (irq_domain_get_of_node(d) != controller) if (irq_domain_get_of_node(d) != controller)
return -EINVAL; return -EINVAL;
if (intsize == 1 && intspec[0] < 16) {
*out_hwirq = intspec[0];
*out_type = IRQ_TYPE_EDGE_RISING;
return 0;
}
if (intsize < 3) if (intsize < 3)
return -EINVAL; return -EINVAL;
@ -344,7 +339,7 @@ static int hip04_irq_domain_xlate(struct irq_domain *d,
*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
return ret; return 0;
} }
static int hip04_irq_starting_cpu(unsigned int cpu) static int hip04_irq_starting_cpu(unsigned int cpu)
@ -361,7 +356,6 @@ static const struct irq_domain_ops hip04_irq_domain_ops = {
static int __init static int __init
hip04_of_init(struct device_node *node, struct device_node *parent) hip04_of_init(struct device_node *node, struct device_node *parent)
{ {
irq_hw_number_t hwirq_base = 16;
int nr_irqs, irq_base, i; int nr_irqs, irq_base, i;
if (WARN_ON(!node)) if (WARN_ON(!node))
@ -390,24 +384,21 @@ hip04_of_init(struct device_node *node, struct device_node *parent)
nr_irqs = HIP04_MAX_IRQS; nr_irqs = HIP04_MAX_IRQS;
hip04_data.nr_irqs = nr_irqs; hip04_data.nr_irqs = nr_irqs;
nr_irqs -= hwirq_base; /* calculate # of irqs to allocate */ irq_base = irq_alloc_descs(-1, 0, nr_irqs, numa_node_id());
irq_base = irq_alloc_descs(-1, hwirq_base, nr_irqs, numa_node_id());
if (irq_base < 0) { if (irq_base < 0) {
pr_err("failed to allocate IRQ numbers\n"); pr_err("failed to allocate IRQ numbers\n");
return -EINVAL; return -EINVAL;
} }
hip04_data.domain = irq_domain_add_legacy(node, nr_irqs, irq_base, hip04_data.domain = irq_domain_add_legacy(node, nr_irqs, irq_base,
hwirq_base, 0,
&hip04_irq_domain_ops, &hip04_irq_domain_ops,
&hip04_data); &hip04_data);
if (WARN_ON(!hip04_data.domain)) if (WARN_ON(!hip04_data.domain))
return -EINVAL; return -EINVAL;
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
set_smp_cross_call(hip04_raise_softirq); set_smp_ipi_range(irq_base, 16);
#endif #endif
set_handle_irq(hip04_handle_irq); set_handle_irq(hip04_handle_irq);