lkml.org 
[lkml]   [2020]   [Sep]   [1]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v3 12/16] irqchip/armada-370-xp: Configure IPIs as standard interrupts
    Date
    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>
    ---
    drivers/irqchip/irq-armada-370-xp.c | 262 +++++++++++++++++++---------
    1 file changed, 178 insertions(+), 84 deletions(-)

    diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
    index c9bdc5221b82..d7eb2e93db8f 100644
    --- a/drivers/irqchip/irq-armada-370-xp.c
    +++ b/drivers/irqchip/irq-armada-370-xp.c
    @@ -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,43 +461,6 @@ static int armada_xp_set_affinity(struct irq_data *d,

    return IRQ_SET_MASK_OK;
    }
    -#endif
    -
    -static struct irq_chip armada_370_xp_irq_chip = {
    - .name = "MPIC",
    - .irq_mask = armada_370_xp_irq_mask,
    - .irq_mask_ack = armada_370_xp_irq_mask,
    - .irq_unmask = armada_370_xp_irq_unmask,
    -#ifdef CONFIG_SMP
    - .irq_set_affinity = armada_xp_set_affinity,
    -#endif
    - .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND,
    -};
    -
    -static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
    - unsigned int virq, irq_hw_number_t hw)
    -{
    - armada_370_xp_irq_mask(irq_get_irq_data(virq));
    - if (!is_percpu_irq(hw))
    - writel(hw, per_cpu_int_base +
    - ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
    - else
    - writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS);
    - irq_set_status_flags(virq, IRQ_LEVEL);
    -
    - if (is_percpu_irq(hw)) {
    - irq_set_percpu_devid(virq);
    - irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
    - handle_percpu_devid_irq);
    - } else {
    - irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
    - handle_level_irq);
    - irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(virq)));
    - }
    - irq_set_probe(virq);
    -
    - return 0;
    -}

    static void armada_xp_mpic_smp_cpu_init(void)
    {
    @@ -383,48 +473,16 @@ static void armada_xp_mpic_smp_cpu_init(void)
    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);

    - /* 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;
    @@ -445,6 +503,8 @@ static void armada_xp_mpic_reenable_percpu(void)

    armada_370_xp_irq_unmask(data);
    }
    +
    + ipi_resume();
    }

    static int armada_xp_mpic_starting_cpu(unsigned int cpu)
    @@ -462,7 +522,46 @@ static int mpic_cascaded_starting_cpu(unsigned int cpu)
    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 = {
    + .name = "MPIC",
    + .irq_mask = armada_370_xp_irq_mask,
    + .irq_mask_ack = armada_370_xp_irq_mask,
    + .irq_unmask = armada_370_xp_irq_unmask,
    +#ifdef CONFIG_SMP
    + .irq_set_affinity = armada_xp_set_affinity,
    #endif
    + .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND,
    +};
    +
    +static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
    + unsigned int virq, irq_hw_number_t hw)
    +{
    + armada_370_xp_irq_mask(irq_get_irq_data(virq));
    + if (!is_percpu_irq(hw))
    + writel(hw, per_cpu_int_base +
    + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
    + else
    + writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS);
    + irq_set_status_flags(virq, IRQ_LEVEL);
    +
    + if (is_percpu_irq(hw)) {
    + irq_set_percpu_devid(virq);
    + irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
    + handle_percpu_devid_irq);
    + } else {
    + irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
    + handle_level_irq);
    + irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(virq)));
    + }
    + irq_set_probe(virq);
    +
    + return 0;
    +}

    static const struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
    .map = armada_370_xp_mpic_irq_map,
    @@ -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);
    --
    2.27.0
    \
     
     \ /
      Last update: 2020-09-01 16:53    [W:5.121 / U:0.012 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site