Re:[PATCH v11 12/14] irqchip/riscv-aplic: Add support for MSI-mode

From: Ben
Date: Thu Nov 02 2023 - 02:39:29 EST



At 2023-10-24 01:27:58, "Anup Patel" <apatel@xxxxxxxxxxxxxxxx> wrote:
>The RISC-V advanced platform-level interrupt controller (APLIC) has
>two modes of operation: 1) Direct mode and 2) MSI mode.
>(For more details, refer https://github.com/riscv/riscv-aia)
>
>In APLIC MSI-mode, wired interrupts are forwared as message signaled
>interrupts (MSIs) to CPUs via IMSIC.
>
>We extend the existing APLIC irqchip driver to support MSI-mode for
>RISC-V platforms having both wired interrupts and MSIs.
>
>Signed-off-by: Anup Patel <apatel@xxxxxxxxxxxxxxxx>
>---
> drivers/irqchip/Kconfig | 6 +
> drivers/irqchip/Makefile | 1 +
> drivers/irqchip/irq-riscv-aplic-main.c | 2 +-
> drivers/irqchip/irq-riscv-aplic-main.h | 8 +
> drivers/irqchip/irq-riscv-aplic-msi.c | 285 +++++++++++++++++++++++++
> 5 files changed, 301 insertions(+), 1 deletion(-)
> create mode 100644 drivers/irqchip/irq-riscv-aplic-msi.c
>
>diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
>index 1996cc6f666a..7adc4dbe07ff 100644
>--- a/drivers/irqchip/Kconfig
>+++ b/drivers/irqchip/Kconfig
>@@ -551,6 +551,12 @@ config RISCV_APLIC
> depends on RISCV
> select IRQ_DOMAIN_HIERARCHY
>
>+config RISCV_APLIC_MSI
>+ bool
>+ depends on RISCV_APLIC
>+ select GENERIC_MSI_IRQ
>+ default RISCV_APLIC
>+
> config RISCV_IMSIC
> bool
> depends on RISCV
>diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
>index 7f8289790ed8..47995fdb2c60 100644
>--- a/drivers/irqchip/Makefile
>+++ b/drivers/irqchip/Makefile
>@@ -96,6 +96,7 @@ obj-$(CONFIG_CSKY_MPINTC) += irq-csky-mpintc.o
> obj-$(CONFIG_CSKY_APB_INTC) += irq-csky-apb-intc.o
> obj-$(CONFIG_RISCV_INTC) += irq-riscv-intc.o
> obj-$(CONFIG_RISCV_APLIC) += irq-riscv-aplic-main.o irq-riscv-aplic-direct.o
>+obj-$(CONFIG_RISCV_APLIC_MSI) += irq-riscv-aplic-msi.o
> obj-$(CONFIG_RISCV_IMSIC) += irq-riscv-imsic-state.o irq-riscv-imsic-early.o irq-riscv-imsic-platform.o
> obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o
> obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o
>diff --git a/drivers/irqchip/irq-riscv-aplic-main.c b/drivers/irqchip/irq-riscv-aplic-main.c
>index 87450708a733..d1b342b66551 100644
>--- a/drivers/irqchip/irq-riscv-aplic-main.c
>+++ b/drivers/irqchip/irq-riscv-aplic-main.c
>@@ -205,7 +205,7 @@ static int aplic_probe(struct platform_device *pdev)
> msi_mode = of_property_present(to_of_node(dev->fwnode),
> "msi-parent");
> if (msi_mode)
>- rc = -ENODEV;
>+ rc = aplic_msi_setup(dev, regs);
> else
> rc = aplic_direct_setup(dev, regs);
> if (rc) {
>diff --git a/drivers/irqchip/irq-riscv-aplic-main.h b/drivers/irqchip/irq-riscv-aplic-main.h
>index 474a04229334..78267ec58098 100644
>--- a/drivers/irqchip/irq-riscv-aplic-main.h
>+++ b/drivers/irqchip/irq-riscv-aplic-main.h
>@@ -41,5 +41,13 @@ void aplic_init_hw_global(struct aplic_priv *priv, bool msi_mode);
> int aplic_setup_priv(struct aplic_priv *priv, struct device *dev,
> void __iomem *regs);
> int aplic_direct_setup(struct device *dev, void __iomem *regs);
>+#ifdef CONFIG_RISCV_APLIC_MSI
>+int aplic_msi_setup(struct device *dev, void __iomem *regs);
>+#else
>+static inline int aplic_msi_setup(struct device *dev, void __iomem *regs)
>+{
>+ return -ENODEV;
>+}
>+#endif
>
> #endif
>diff --git a/drivers/irqchip/irq-riscv-aplic-msi.c b/drivers/irqchip/irq-riscv-aplic-msi.c
>new file mode 100644
>index 000000000000..086d00e0429e
>--- /dev/null
>+++ b/drivers/irqchip/irq-riscv-aplic-msi.c
>@@ -0,0 +1,285 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/*
>+ * Copyright (C) 2021 Western Digital Corporation or its affiliates.
>+ * Copyright (C) 2022 Ventana Micro Systems Inc.
>+ */
>+
>+#include <linux/bitops.h>
>+#include <linux/cpu.h>
>+#include <linux/interrupt.h>
>+#include <linux/irqchip.h>
>+#include <linux/irqchip/riscv-aplic.h>
>+#include <linux/irqchip/riscv-imsic.h>
>+#include <linux/module.h>
>+#include <linux/msi.h>
>+#include <linux/of_irq.h>
>+#include <linux/platform_device.h>
>+#include <linux/printk.h>
>+#include <linux/smp.h>
>+
>+#include "irq-riscv-aplic-main.h"
>+
>+static void aplic_msi_irq_unmask(struct irq_data *d)
>+{
>+ aplic_irq_unmask(d);
>+ irq_chip_unmask_parent(d);
>+}
>+
>+static void aplic_msi_irq_mask(struct irq_data *d)
>+{
>+ aplic_irq_mask(d);
>+ irq_chip_mask_parent(d);
>+}
>+
>+static void aplic_msi_irq_eoi(struct irq_data *d)
>+{
>+ struct aplic_priv *priv = irq_data_get_irq_chip_data(d);
>+ u32 reg_off, reg_mask;
>+
>+ /*
>+ * EOI handling only required only for level-triggered
>+ * interrupts in APLIC MSI mode.
>+ */
>+
>+ reg_off = APLIC_CLRIP_BASE + ((d->hwirq / APLIC_IRQBITS_PER_REG) * 4);
>+ reg_mask = BIT(d->hwirq % APLIC_IRQBITS_PER_REG);
>+ switch (irqd_get_trigger_type(d)) {
>+ case IRQ_TYPE_LEVEL_LOW:
>+ if (!(readl(priv->regs + reg_off) & reg_mask))
>+ writel(d->hwirq, priv->regs + APLIC_SETIPNUM_LE);
>+ break;
>+ case IRQ_TYPE_LEVEL_HIGH:
>+ if (readl(priv->regs + reg_off) & reg_mask)
>+ writel(d->hwirq, priv->regs + APLIC_SETIPNUM_LE);
>+ break;
>+ }
>+}
>+
>+static struct irq_chip aplic_msi_chip = {
>+ .name = "APLIC-MSI",
>+ .irq_mask = aplic_msi_irq_mask,
>+ .irq_unmask = aplic_msi_irq_unmask,
>+ .irq_set_type = aplic_irq_set_type,
>+ .irq_eoi = aplic_msi_irq_eoi,
>+#ifdef CONFIG_SMP
>+ .irq_set_affinity = irq_chip_set_affinity_parent,
>+#endif
>+ .flags = IRQCHIP_SET_TYPE_MASKED |
>+ IRQCHIP_SKIP_SET_WAKE |
>+ IRQCHIP_MASK_ON_SUSPEND,
>+};
>+
>+static int aplic_msi_irqdomain_translate(struct irq_domain *d,
>+ struct irq_fwspec *fwspec,
>+ unsigned long *hwirq,
>+ unsigned int *type)
>+{
>+ struct aplic_priv *priv = platform_msi_get_host_data(d);
>+
>+ return aplic_irqdomain_translate(fwspec, priv->gsi_base, hwirq, type);
>+}
>+
>+static int aplic_msi_irqdomain_alloc(struct irq_domain *domain,
>+ unsigned int virq, unsigned int nr_irqs,
>+ void *arg)
>+{
>+ int i, ret;
>+ unsigned int type;
>+ irq_hw_number_t hwirq;
>+ struct irq_fwspec *fwspec = arg;
>+ struct aplic_priv *priv = platform_msi_get_host_data(domain);
>+
>+ ret = aplic_irqdomain_translate(fwspec, priv->gsi_base, &hwirq, &type);
>+ if (ret)
>+ return ret;
>+
>+ ret = platform_msi_device_domain_alloc(domain, virq, nr_irqs);
>+ if (ret)
>+ return ret;
>+
>+ for (i = 0; i < nr_irqs; i++) {
>+ irq_domain_set_info(domain, virq + i, hwirq + i,
>+ &aplic_msi_chip, priv, handle_fasteoi_irq,
>+ NULL, NULL);
>+ /*
>+ * APLIC does not implement irq_disable() so Linux interrupt
>+ * subsystem will take a lazy approach for disabling an APLIC
>+ * interrupt. This means APLIC interrupts are left unmasked
>+ * upon system suspend and interrupts are not processed
>+ * immediately upon system wake up. To tackle this, we disable
>+ * the lazy approach for all APLIC interrupts.
>+ */
>+ irq_set_status_flags(virq + i, IRQ_DISABLE_UNLAZY);

>+ }


For platfrom MSI irq, it will call irq_domain_set_info() and irq_set_status_flags() twice, the first one is here:
platform_msi_device_domain_alloc->msi_domain_populate_irqs->irq_domain_alloc_irqs_hierarchy->imsic_irq_domain_alloc->irq_domain_set_info


so i think here this for(...) is not necessary, can be removed.