[PATCH v7 15/17] irqchip/riscv-imsic: Add ACPI support

Anup Patel anup at brainfault.org
Tue Aug 6 08:30:13 PDT 2024


On Mon, Jul 29, 2024 at 7:54 PM Sunil V L <sunilvl at ventanamicro.com> wrote:
>
> RISC-V IMSIC interrupt controller provides IPI and MSI support.
> Currently, DT based drivers setup the IPI feature early during boot but
> defer setting up the MSI functionality. However, in ACPI systems, PCI
> subsystem is probed early and assume MSI controller is already setup.
> Hence, both IPI and MSI features need to be initialized early itself.
>
> Signed-off-by: Sunil V L <sunilvl at ventanamicro.com>

LGTM.

Reviewed-by: Anup Patel <anup at brainfault.org>

Regards,
Anup


> ---
>  drivers/irqchip/irq-riscv-imsic-early.c    | 64 +++++++++++++++++++++-
>  drivers/irqchip/irq-riscv-imsic-platform.c | 32 +++++++++--
>  drivers/irqchip/irq-riscv-imsic-state.c    | 57 +++++++++++--------
>  drivers/irqchip/irq-riscv-imsic-state.h    |  2 +-
>  include/linux/irqchip/riscv-imsic.h        |  9 +++
>  5 files changed, 134 insertions(+), 30 deletions(-)
>
> diff --git a/drivers/irqchip/irq-riscv-imsic-early.c b/drivers/irqchip/irq-riscv-imsic-early.c
> index 4fbb37074d29..c5c2e6929a2f 100644
> --- a/drivers/irqchip/irq-riscv-imsic-early.c
> +++ b/drivers/irqchip/irq-riscv-imsic-early.c
> @@ -5,13 +5,16 @@
>   */
>
>  #define pr_fmt(fmt) "riscv-imsic: " fmt
> +#include <linux/acpi.h>
>  #include <linux/cpu.h>
>  #include <linux/interrupt.h>
>  #include <linux/io.h>
>  #include <linux/irq.h>
>  #include <linux/irqchip.h>
>  #include <linux/irqchip/chained_irq.h>
> +#include <linux/irqchip/riscv-imsic.h>
>  #include <linux/module.h>
> +#include <linux/pci.h>
>  #include <linux/spinlock.h>
>  #include <linux/smp.h>
>
> @@ -182,7 +185,7 @@ static int __init imsic_early_dt_init(struct device_node *node, struct device_no
>         int rc;
>
>         /* Setup IMSIC state */
> -       rc = imsic_setup_state(fwnode);
> +       rc = imsic_setup_state(fwnode, NULL);
>         if (rc) {
>                 pr_err("%pfwP: failed to setup state (error %d)\n", fwnode, rc);
>                 return rc;
> @@ -199,3 +202,62 @@ static int __init imsic_early_dt_init(struct device_node *node, struct device_no
>  }
>
>  IRQCHIP_DECLARE(riscv_imsic, "riscv,imsics", imsic_early_dt_init);
> +
> +#ifdef CONFIG_ACPI
> +
> +static struct fwnode_handle *imsic_acpi_fwnode;
> +
> +struct fwnode_handle *imsic_acpi_get_fwnode(struct device *dev)
> +{
> +       return imsic_acpi_fwnode;
> +}
> +
> +static int __init imsic_early_acpi_init(union acpi_subtable_headers *header,
> +                                       const unsigned long end)
> +{
> +       struct acpi_madt_imsic *imsic = (struct acpi_madt_imsic *)header;
> +       int rc;
> +
> +       imsic_acpi_fwnode = irq_domain_alloc_named_fwnode("imsic");
> +       if (!imsic_acpi_fwnode) {
> +               pr_err("unable to allocate IMSIC FW node\n");
> +               return -ENOMEM;
> +       }
> +
> +       /* Setup IMSIC state */
> +       rc = imsic_setup_state(imsic_acpi_fwnode, imsic);
> +       if (rc) {
> +               pr_err("%pfwP: failed to setup state (error %d)\n", imsic_acpi_fwnode, rc);
> +               return rc;
> +       }
> +
> +       /* Do early setup of IMSIC state and IPIs */
> +       rc = imsic_early_probe(imsic_acpi_fwnode);
> +       if (rc) {
> +               irq_domain_free_fwnode(imsic_acpi_fwnode);
> +               imsic_acpi_fwnode = NULL;
> +               return rc;
> +       }
> +
> +       rc = imsic_platform_acpi_probe(imsic_acpi_fwnode);
> +
> +#ifdef CONFIG_PCI
> +       if (!rc)
> +               pci_msi_register_fwnode_provider(&imsic_acpi_get_fwnode);
> +#endif
> +
> +       if (rc)
> +               pr_err("%pfwP: failed to register IMSIC for MSI functionality (error %d)\n",
> +                      imsic_acpi_fwnode, rc);
> +
> +       /*
> +        * Even if imsic_platform_acpi_probe() fails, the IPI part of IMSIC can
> +        * continue to work. So, no need to return failure. This is similar to
> +        * DT where IPI works but MSI probe fails for some reason.
> +        */
> +       return 0;
> +}
> +
> +IRQCHIP_ACPI_DECLARE(riscv_imsic, ACPI_MADT_TYPE_IMSIC, NULL,
> +                    1, imsic_early_acpi_init);
> +#endif
> diff --git a/drivers/irqchip/irq-riscv-imsic-platform.c b/drivers/irqchip/irq-riscv-imsic-platform.c
> index 11723a763c10..64905e6f52d7 100644
> --- a/drivers/irqchip/irq-riscv-imsic-platform.c
> +++ b/drivers/irqchip/irq-riscv-imsic-platform.c
> @@ -5,6 +5,7 @@
>   */
>
>  #define pr_fmt(fmt) "riscv-imsic: " fmt
> +#include <linux/acpi.h>
>  #include <linux/bitmap.h>
>  #include <linux/cpu.h>
>  #include <linux/interrupt.h>
> @@ -348,18 +349,37 @@ int imsic_irqdomain_init(void)
>         return 0;
>  }
>
> -static int imsic_platform_probe(struct platform_device *pdev)
> +static int imsic_platform_probe_common(struct fwnode_handle *fwnode)
>  {
> -       struct device *dev = &pdev->dev;
> -
> -       if (imsic && imsic->fwnode != dev->fwnode) {
> -               dev_err(dev, "fwnode mismatch\n");
> +       if (imsic && imsic->fwnode != fwnode) {
> +               pr_err("%pfwP: fwnode mismatch\n", fwnode);
>                 return -ENODEV;
>         }
>
>         return imsic_irqdomain_init();
>  }
>
> +static int imsic_platform_dt_probe(struct platform_device *pdev)
> +{
> +       return imsic_platform_probe_common(pdev->dev.fwnode);
> +}
> +
> +#ifdef CONFIG_ACPI
> +
> +/*
> + *  On ACPI based systems, PCI enumeration happens early during boot in
> + *  acpi_scan_init(). PCI enumeration expects MSI domain setup before
> + *  it calls pci_set_msi_domain(). Hence, unlike in DT where
> + *  imsic-platform drive probe happens late during boot, ACPI based
> + *  systems need to setup the MSI domain early.
> + */
> +int imsic_platform_acpi_probe(struct fwnode_handle *fwnode)
> +{
> +       return imsic_platform_probe_common(fwnode);
> +}
> +
> +#endif
> +
>  static const struct of_device_id imsic_platform_match[] = {
>         { .compatible = "riscv,imsics" },
>         {}
> @@ -370,6 +390,6 @@ static struct platform_driver imsic_platform_driver = {
>                 .name           = "riscv-imsic",
>                 .of_match_table = imsic_platform_match,
>         },
> -       .probe = imsic_platform_probe,
> +       .probe = imsic_platform_dt_probe,
>  };
>  builtin_platform_driver(imsic_platform_driver);
> diff --git a/drivers/irqchip/irq-riscv-imsic-state.c b/drivers/irqchip/irq-riscv-imsic-state.c
> index f9e70832863a..73faa64bffda 100644
> --- a/drivers/irqchip/irq-riscv-imsic-state.c
> +++ b/drivers/irqchip/irq-riscv-imsic-state.c
> @@ -5,6 +5,7 @@
>   */
>
>  #define pr_fmt(fmt) "riscv-imsic: " fmt
> +#include <linux/acpi.h>
>  #include <linux/cpu.h>
>  #include <linux/bitmap.h>
>  #include <linux/interrupt.h>
> @@ -564,18 +565,36 @@ static int __init imsic_populate_global_dt(struct fwnode_handle *fwnode,
>         return 0;
>  }
>
> +static int __init imsic_populate_global_acpi(struct fwnode_handle *fwnode,
> +                                            struct imsic_global_config *global,
> +                                            u32 *nr_parent_irqs, void *opaque)
> +{
> +       struct acpi_madt_imsic *imsic = (struct acpi_madt_imsic *)opaque;
> +
> +       global->guest_index_bits = imsic->guest_index_bits;
> +       global->hart_index_bits = imsic->hart_index_bits;
> +       global->group_index_bits = imsic->group_index_bits;
> +       global->group_index_shift = imsic->group_index_shift;
> +       global->nr_ids = imsic->num_ids;
> +       global->nr_guest_ids = imsic->num_guest_ids;
> +       return 0;
> +}
> +
>  static int __init imsic_get_parent_hartid(struct fwnode_handle *fwnode,
>                                           u32 index, unsigned long *hartid)
>  {
>         struct of_phandle_args parent;
>         int rc;
>
> -       /*
> -        * Currently, only OF fwnode is supported so extend this
> -        * function for ACPI support.
> -        */
> -       if (!is_of_node(fwnode))
> -               return -EINVAL;
> +       if (!is_of_node(fwnode)) {
> +               if (hartid)
> +                       *hartid = acpi_get_intc_index_hartid(index);
> +
> +               if (!hartid || (*hartid == INVALID_HARTID))
> +                       return -EINVAL;
> +
> +               return 0;
> +       }
>
>         rc = of_irq_parse_one(to_of_node(fwnode), index, &parent);
>         if (rc)
> @@ -594,12 +613,8 @@ static int __init imsic_get_parent_hartid(struct fwnode_handle *fwnode,
>  static int __init imsic_get_mmio_resource(struct fwnode_handle *fwnode,
>                                           u32 index, struct resource *res)
>  {
> -       /*
> -        * Currently, only OF fwnode is supported so extend this
> -        * function for ACPI support.
> -        */
>         if (!is_of_node(fwnode))
> -               return -EINVAL;
> +               return acpi_get_imsic_mmio_info(index, res);
>
>         return of_address_to_resource(to_of_node(fwnode), index, res);
>  }
> @@ -607,20 +622,14 @@ static int __init imsic_get_mmio_resource(struct fwnode_handle *fwnode,
>  static int __init imsic_parse_fwnode(struct fwnode_handle *fwnode,
>                                      struct imsic_global_config *global,
>                                      u32 *nr_parent_irqs,
> -                                    u32 *nr_mmios)
> +                                    u32 *nr_mmios,
> +                                    void *opaque)
>  {
>         unsigned long hartid;
>         struct resource res;
>         int rc;
>         u32 i;
>
> -       /*
> -        * Currently, only OF fwnode is supported so extend this
> -        * function for ACPI support.
> -        */
> -       if (!is_of_node(fwnode))
> -               return -EINVAL;
> -
>         *nr_parent_irqs = 0;
>         *nr_mmios = 0;
>
> @@ -632,7 +641,11 @@ static int __init imsic_parse_fwnode(struct fwnode_handle *fwnode,
>                 return -EINVAL;
>         }
>
> -       rc = imsic_populate_global_dt(fwnode, global, nr_parent_irqs);
> +       if (is_of_node(fwnode))
> +               rc = imsic_populate_global_dt(fwnode, global, nr_parent_irqs);
> +       else
> +               rc = imsic_populate_global_acpi(fwnode, global, nr_parent_irqs, opaque);
> +
>         if (rc)
>                 return rc;
>
> @@ -701,7 +714,7 @@ static int __init imsic_parse_fwnode(struct fwnode_handle *fwnode,
>         return 0;
>  }
>
> -int __init imsic_setup_state(struct fwnode_handle *fwnode)
> +int __init imsic_setup_state(struct fwnode_handle *fwnode, void *opaque)
>  {
>         u32 i, j, index, nr_parent_irqs, nr_mmios, nr_handlers = 0;
>         struct imsic_global_config *global;
> @@ -742,7 +755,7 @@ int __init imsic_setup_state(struct fwnode_handle *fwnode)
>         }
>
>         /* Parse IMSIC fwnode */
> -       rc = imsic_parse_fwnode(fwnode, global, &nr_parent_irqs, &nr_mmios);
> +       rc = imsic_parse_fwnode(fwnode, global, &nr_parent_irqs, &nr_mmios, opaque);
>         if (rc)
>                 goto out_free_local;
>
> diff --git a/drivers/irqchip/irq-riscv-imsic-state.h b/drivers/irqchip/irq-riscv-imsic-state.h
> index 5ae2f69b035b..391e44280827 100644
> --- a/drivers/irqchip/irq-riscv-imsic-state.h
> +++ b/drivers/irqchip/irq-riscv-imsic-state.h
> @@ -102,7 +102,7 @@ void imsic_vector_debug_show_summary(struct seq_file *m, int ind);
>
>  void imsic_state_online(void);
>  void imsic_state_offline(void);
> -int imsic_setup_state(struct fwnode_handle *fwnode);
> +int imsic_setup_state(struct fwnode_handle *fwnode, void *opaque);
>  int imsic_irqdomain_init(void);
>
>  #endif
> diff --git a/include/linux/irqchip/riscv-imsic.h b/include/linux/irqchip/riscv-imsic.h
> index faf0b800b1b0..7494952c5518 100644
> --- a/include/linux/irqchip/riscv-imsic.h
> +++ b/include/linux/irqchip/riscv-imsic.h
> @@ -8,6 +8,8 @@
>
>  #include <linux/types.h>
>  #include <linux/bitops.h>
> +#include <linux/device.h>
> +#include <linux/fwnode.h>
>  #include <asm/csr.h>
>
>  #define IMSIC_MMIO_PAGE_SHIFT          12
> @@ -84,4 +86,11 @@ static inline const struct imsic_global_config *imsic_get_global_config(void)
>
>  #endif
>
> +#ifdef CONFIG_ACPI
> +int imsic_platform_acpi_probe(struct fwnode_handle *fwnode);
> +struct fwnode_handle *imsic_acpi_get_fwnode(struct device *dev);
> +#else
> +static inline struct fwnode_handle *imsic_acpi_get_fwnode(struct device *dev) { return NULL; }
> +#endif
> +
>  #endif
> --
> 2.43.0
>



More information about the linux-riscv mailing list