irqchip/armada-370-xp: Configure IPIs as standard interrupts
To introduce IPIs as standard interrupts to the Armada 370-XP driver, let's allocate a completely separate irqdomain and irqchip combo that lives parallel to the "standard" one. This effectively should be modelled as a chained interrupt controller, but the code is in such a state that it is pretty hard to shoehorn, as it would require the rewrite of the MSI layer as well. Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
parent
a2df12c589
commit
f02147dd02
@ -310,7 +310,134 @@ static inline int armada_370_xp_msi_init(struct device_node *node,
|
||||
}
|
||||
#endif
|
||||
|
||||
static void armada_xp_mpic_perf_init(void)
|
||||
{
|
||||
unsigned long cpuid = cpu_logical_map(smp_processor_id());
|
||||
|
||||
/* Enable Performance Counter Overflow interrupts */
|
||||
writel(ARMADA_370_XP_INT_CAUSE_PERF(cpuid),
|
||||
per_cpu_int_base + ARMADA_370_XP_INT_FABRIC_MASK_OFFS);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static struct irq_domain *ipi_domain;
|
||||
|
||||
static void armada_370_xp_ipi_mask(struct irq_data *d)
|
||||
{
|
||||
u32 reg;
|
||||
reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
|
||||
reg &= ~BIT(d->hwirq);
|
||||
writel(reg, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
|
||||
}
|
||||
|
||||
static void armada_370_xp_ipi_unmask(struct irq_data *d)
|
||||
{
|
||||
u32 reg;
|
||||
reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
|
||||
reg |= BIT(d->hwirq);
|
||||
writel(reg, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
|
||||
}
|
||||
|
||||
static void armada_370_xp_ipi_send_mask(struct irq_data *d,
|
||||
const struct cpumask *mask)
|
||||
{
|
||||
unsigned long map = 0;
|
||||
int cpu;
|
||||
|
||||
/* Convert our logical CPU mask into a physical one. */
|
||||
for_each_cpu(cpu, mask)
|
||||
map |= 1 << cpu_logical_map(cpu);
|
||||
|
||||
/*
|
||||
* Ensure that stores to Normal memory are visible to the
|
||||
* other CPUs before issuing the IPI.
|
||||
*/
|
||||
dsb();
|
||||
|
||||
/* submit softirq */
|
||||
writel((map << 8) | d->hwirq, main_int_base +
|
||||
ARMADA_370_XP_SW_TRIG_INT_OFFS);
|
||||
}
|
||||
|
||||
static void armada_370_xp_ipi_eoi(struct irq_data *d)
|
||||
{
|
||||
writel(~BIT(d->hwirq), per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
|
||||
}
|
||||
|
||||
static struct irq_chip ipi_irqchip = {
|
||||
.name = "IPI",
|
||||
.irq_mask = armada_370_xp_ipi_mask,
|
||||
.irq_unmask = armada_370_xp_ipi_unmask,
|
||||
.irq_eoi = armada_370_xp_ipi_eoi,
|
||||
.ipi_send_mask = armada_370_xp_ipi_send_mask,
|
||||
};
|
||||
|
||||
static int armada_370_xp_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, &ipi_irqchip,
|
||||
d->host_data,
|
||||
handle_percpu_devid_fasteoi_ipi,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void armada_370_xp_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 = armada_370_xp_ipi_alloc,
|
||||
.free = armada_370_xp_ipi_free,
|
||||
};
|
||||
|
||||
static void ipi_resume(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IPI_DOORBELL_END; i++) {
|
||||
int irq;
|
||||
|
||||
irq = irq_find_mapping(ipi_domain, i);
|
||||
if (irq <= 0)
|
||||
continue;
|
||||
if (irq_percpu_is_enabled(irq)) {
|
||||
struct irq_data *d;
|
||||
d = irq_domain_get_irq_data(ipi_domain, irq);
|
||||
armada_370_xp_ipi_unmask(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static __init void armada_xp_ipi_init(struct device_node *node)
|
||||
{
|
||||
int base_ipi;
|
||||
|
||||
ipi_domain = irq_domain_create_linear(of_node_to_fwnode(node),
|
||||
IPI_DOORBELL_END,
|
||||
&ipi_domain_ops, NULL);
|
||||
if (WARN_ON(!ipi_domain))
|
||||
return;
|
||||
|
||||
irq_domain_update_bus_token(ipi_domain, DOMAIN_BUS_IPI);
|
||||
base_ipi = __irq_domain_alloc_irqs(ipi_domain, -1, IPI_DOORBELL_END,
|
||||
NUMA_NO_NODE, NULL, false, NULL);
|
||||
if (WARN_ON(!base_ipi))
|
||||
return;
|
||||
|
||||
set_smp_ipi_range(base_ipi, IPI_DOORBELL_END);
|
||||
}
|
||||
|
||||
static DEFINE_RAW_SPINLOCK(irq_controller_lock);
|
||||
|
||||
static int armada_xp_set_affinity(struct irq_data *d,
|
||||
@ -334,6 +461,70 @@ static int armada_xp_set_affinity(struct irq_data *d,
|
||||
|
||||
return IRQ_SET_MASK_OK;
|
||||
}
|
||||
|
||||
static void armada_xp_mpic_smp_cpu_init(void)
|
||||
{
|
||||
u32 control;
|
||||
int nr_irqs, i;
|
||||
|
||||
control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL);
|
||||
nr_irqs = (control >> 2) & 0x3ff;
|
||||
|
||||
for (i = 0; i < nr_irqs; i++)
|
||||
writel(i, per_cpu_int_base + ARMADA_370_XP_INT_SET_MASK_OFFS);
|
||||
|
||||
/* Disable all IPIs */
|
||||
writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
|
||||
|
||||
/* Clear pending IPIs */
|
||||
writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
|
||||
|
||||
/* Unmask IPI interrupt */
|
||||
writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
|
||||
}
|
||||
|
||||
static void armada_xp_mpic_reenable_percpu(void)
|
||||
{
|
||||
unsigned int irq;
|
||||
|
||||
/* Re-enable per-CPU interrupts that were enabled before suspend */
|
||||
for (irq = 0; irq < ARMADA_370_XP_MAX_PER_CPU_IRQS; irq++) {
|
||||
struct irq_data *data;
|
||||
int virq;
|
||||
|
||||
virq = irq_linear_revmap(armada_370_xp_mpic_domain, irq);
|
||||
if (virq == 0)
|
||||
continue;
|
||||
|
||||
data = irq_get_irq_data(virq);
|
||||
|
||||
if (!irq_percpu_is_enabled(virq))
|
||||
continue;
|
||||
|
||||
armada_370_xp_irq_unmask(data);
|
||||
}
|
||||
|
||||
ipi_resume();
|
||||
}
|
||||
|
||||
static int armada_xp_mpic_starting_cpu(unsigned int cpu)
|
||||
{
|
||||
armada_xp_mpic_perf_init();
|
||||
armada_xp_mpic_smp_cpu_init();
|
||||
armada_xp_mpic_reenable_percpu();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpic_cascaded_starting_cpu(unsigned int cpu)
|
||||
{
|
||||
armada_xp_mpic_perf_init();
|
||||
armada_xp_mpic_reenable_percpu();
|
||||
enable_percpu_irq(parent_irq, IRQ_TYPE_NONE);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static void armada_xp_mpic_smp_cpu_init(void) {}
|
||||
static void ipi_resume(void) {}
|
||||
#endif
|
||||
|
||||
static struct irq_chip armada_370_xp_irq_chip = {
|
||||
@ -372,98 +563,6 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void armada_xp_mpic_smp_cpu_init(void)
|
||||
{
|
||||
u32 control;
|
||||
int nr_irqs, i;
|
||||
|
||||
control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL);
|
||||
nr_irqs = (control >> 2) & 0x3ff;
|
||||
|
||||
for (i = 0; i < nr_irqs; i++)
|
||||
writel(i, per_cpu_int_base + ARMADA_370_XP_INT_SET_MASK_OFFS);
|
||||
|
||||
/* Clear pending IPIs */
|
||||
writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
|
||||
|
||||
/* Enable first 8 IPIs */
|
||||
writel(IPI_DOORBELL_MASK, per_cpu_int_base +
|
||||
ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
|
||||
|
||||
/* Unmask IPI interrupt */
|
||||
writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
|
||||
}
|
||||
|
||||
static void armada_xp_mpic_perf_init(void)
|
||||
{
|
||||
unsigned long cpuid = cpu_logical_map(smp_processor_id());
|
||||
|
||||
/* Enable Performance Counter Overflow interrupts */
|
||||
writel(ARMADA_370_XP_INT_CAUSE_PERF(cpuid),
|
||||
per_cpu_int_base + ARMADA_370_XP_INT_FABRIC_MASK_OFFS);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static void armada_mpic_send_doorbell(const struct cpumask *mask,
|
||||
unsigned int irq)
|
||||
{
|
||||
int cpu;
|
||||
unsigned long map = 0;
|
||||
|
||||
/* Convert our logical CPU mask into a physical one. */
|
||||
for_each_cpu(cpu, mask)
|
||||
map |= 1 << cpu_logical_map(cpu);
|
||||
|
||||
/*
|
||||
* Ensure that stores to Normal memory are visible to the
|
||||
* other CPUs before issuing the IPI.
|
||||
*/
|
||||
dsb();
|
||||
|
||||
/* submit softirq */
|
||||
writel((map << 8) | irq, main_int_base +
|
||||
ARMADA_370_XP_SW_TRIG_INT_OFFS);
|
||||
}
|
||||
|
||||
static void armada_xp_mpic_reenable_percpu(void)
|
||||
{
|
||||
unsigned int irq;
|
||||
|
||||
/* Re-enable per-CPU interrupts that were enabled before suspend */
|
||||
for (irq = 0; irq < ARMADA_370_XP_MAX_PER_CPU_IRQS; irq++) {
|
||||
struct irq_data *data;
|
||||
int virq;
|
||||
|
||||
virq = irq_linear_revmap(armada_370_xp_mpic_domain, irq);
|
||||
if (virq == 0)
|
||||
continue;
|
||||
|
||||
data = irq_get_irq_data(virq);
|
||||
|
||||
if (!irq_percpu_is_enabled(virq))
|
||||
continue;
|
||||
|
||||
armada_370_xp_irq_unmask(data);
|
||||
}
|
||||
}
|
||||
|
||||
static int armada_xp_mpic_starting_cpu(unsigned int cpu)
|
||||
{
|
||||
armada_xp_mpic_perf_init();
|
||||
armada_xp_mpic_smp_cpu_init();
|
||||
armada_xp_mpic_reenable_percpu();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpic_cascaded_starting_cpu(unsigned int cpu)
|
||||
{
|
||||
armada_xp_mpic_perf_init();
|
||||
armada_xp_mpic_reenable_percpu();
|
||||
enable_percpu_irq(parent_irq, IRQ_TYPE_NONE);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
|
||||
.map = armada_370_xp_mpic_irq_map,
|
||||
.xlate = irq_domain_xlate_onecell,
|
||||
@ -562,22 +661,15 @@ armada_370_xp_handle_irq(struct pt_regs *regs)
|
||||
#ifdef CONFIG_SMP
|
||||
/* IPI Handling */
|
||||
if (irqnr == 0) {
|
||||
u32 ipimask, ipinr;
|
||||
unsigned long ipimask;
|
||||
int ipi;
|
||||
|
||||
ipimask = readl_relaxed(per_cpu_int_base +
|
||||
ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
|
||||
& IPI_DOORBELL_MASK;
|
||||
|
||||
writel(~ipimask, per_cpu_int_base +
|
||||
ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
|
||||
|
||||
/* Handle all pending doorbells */
|
||||
for (ipinr = IPI_DOORBELL_START;
|
||||
ipinr < IPI_DOORBELL_END; ipinr++) {
|
||||
if (ipimask & (0x1 << ipinr))
|
||||
handle_IPI(ipinr, regs);
|
||||
}
|
||||
continue;
|
||||
for_each_set_bit(ipi, &ipimask, IPI_DOORBELL_END)
|
||||
handle_domain_irq(ipi_domain, ipi, regs);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -636,6 +728,8 @@ static void armada_370_xp_mpic_resume(void)
|
||||
writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
|
||||
if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK)
|
||||
writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
|
||||
|
||||
ipi_resume();
|
||||
}
|
||||
|
||||
static struct syscore_ops armada_370_xp_mpic_syscore_ops = {
|
||||
@ -691,7 +785,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
|
||||
irq_set_default_host(armada_370_xp_mpic_domain);
|
||||
set_handle_irq(armada_370_xp_handle_irq);
|
||||
#ifdef CONFIG_SMP
|
||||
set_smp_cross_call(armada_mpic_send_doorbell);
|
||||
armada_xp_ipi_init(node);
|
||||
cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_ARMADA_XP_STARTING,
|
||||
"irqchip/armada/ipi:starting",
|
||||
armada_xp_mpic_starting_cpu, NULL);
|
||||
|
Loading…
Reference in New Issue
Block a user