Skip to content

Commit fbe826b

Browse files
vlsunilrafaeljw
authored andcommitted
irqchip/riscv-imsic: Add ACPI support
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 <[email protected]> Reviewed-by: Anup Patel <[email protected]> Tested-by: Björn Töpel <[email protected]> Acked-by: Thomas Gleixner <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent aa143df commit fbe826b

File tree

5 files changed

+134
-30
lines changed

5 files changed

+134
-30
lines changed

drivers/irqchip/irq-riscv-imsic-early.c

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@
55
*/
66

77
#define pr_fmt(fmt) "riscv-imsic: " fmt
8+
#include <linux/acpi.h>
89
#include <linux/cpu.h>
910
#include <linux/interrupt.h>
1011
#include <linux/io.h>
1112
#include <linux/irq.h>
1213
#include <linux/irqchip.h>
1314
#include <linux/irqchip/chained_irq.h>
15+
#include <linux/irqchip/riscv-imsic.h>
1416
#include <linux/module.h>
17+
#include <linux/pci.h>
1518
#include <linux/spinlock.h>
1619
#include <linux/smp.h>
1720

@@ -182,7 +185,7 @@ static int __init imsic_early_dt_init(struct device_node *node, struct device_no
182185
int rc;
183186

184187
/* Setup IMSIC state */
185-
rc = imsic_setup_state(fwnode);
188+
rc = imsic_setup_state(fwnode, NULL);
186189
if (rc) {
187190
pr_err("%pfwP: failed to setup state (error %d)\n", fwnode, rc);
188191
return rc;
@@ -199,3 +202,62 @@ static int __init imsic_early_dt_init(struct device_node *node, struct device_no
199202
}
200203

201204
IRQCHIP_DECLARE(riscv_imsic, "riscv,imsics", imsic_early_dt_init);
205+
206+
#ifdef CONFIG_ACPI
207+
208+
static struct fwnode_handle *imsic_acpi_fwnode;
209+
210+
struct fwnode_handle *imsic_acpi_get_fwnode(struct device *dev)
211+
{
212+
return imsic_acpi_fwnode;
213+
}
214+
215+
static int __init imsic_early_acpi_init(union acpi_subtable_headers *header,
216+
const unsigned long end)
217+
{
218+
struct acpi_madt_imsic *imsic = (struct acpi_madt_imsic *)header;
219+
int rc;
220+
221+
imsic_acpi_fwnode = irq_domain_alloc_named_fwnode("imsic");
222+
if (!imsic_acpi_fwnode) {
223+
pr_err("unable to allocate IMSIC FW node\n");
224+
return -ENOMEM;
225+
}
226+
227+
/* Setup IMSIC state */
228+
rc = imsic_setup_state(imsic_acpi_fwnode, imsic);
229+
if (rc) {
230+
pr_err("%pfwP: failed to setup state (error %d)\n", imsic_acpi_fwnode, rc);
231+
return rc;
232+
}
233+
234+
/* Do early setup of IMSIC state and IPIs */
235+
rc = imsic_early_probe(imsic_acpi_fwnode);
236+
if (rc) {
237+
irq_domain_free_fwnode(imsic_acpi_fwnode);
238+
imsic_acpi_fwnode = NULL;
239+
return rc;
240+
}
241+
242+
rc = imsic_platform_acpi_probe(imsic_acpi_fwnode);
243+
244+
#ifdef CONFIG_PCI
245+
if (!rc)
246+
pci_msi_register_fwnode_provider(&imsic_acpi_get_fwnode);
247+
#endif
248+
249+
if (rc)
250+
pr_err("%pfwP: failed to register IMSIC for MSI functionality (error %d)\n",
251+
imsic_acpi_fwnode, rc);
252+
253+
/*
254+
* Even if imsic_platform_acpi_probe() fails, the IPI part of IMSIC can
255+
* continue to work. So, no need to return failure. This is similar to
256+
* DT where IPI works but MSI probe fails for some reason.
257+
*/
258+
return 0;
259+
}
260+
261+
IRQCHIP_ACPI_DECLARE(riscv_imsic, ACPI_MADT_TYPE_IMSIC, NULL,
262+
1, imsic_early_acpi_init);
263+
#endif

drivers/irqchip/irq-riscv-imsic-platform.c

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
#define pr_fmt(fmt) "riscv-imsic: " fmt
8+
#include <linux/acpi.h>
89
#include <linux/bitmap.h>
910
#include <linux/cpu.h>
1011
#include <linux/interrupt.h>
@@ -348,18 +349,37 @@ int imsic_irqdomain_init(void)
348349
return 0;
349350
}
350351

351-
static int imsic_platform_probe(struct platform_device *pdev)
352+
static int imsic_platform_probe_common(struct fwnode_handle *fwnode)
352353
{
353-
struct device *dev = &pdev->dev;
354-
355-
if (imsic && imsic->fwnode != dev->fwnode) {
356-
dev_err(dev, "fwnode mismatch\n");
354+
if (imsic && imsic->fwnode != fwnode) {
355+
pr_err("%pfwP: fwnode mismatch\n", fwnode);
357356
return -ENODEV;
358357
}
359358

360359
return imsic_irqdomain_init();
361360
}
362361

362+
static int imsic_platform_dt_probe(struct platform_device *pdev)
363+
{
364+
return imsic_platform_probe_common(pdev->dev.fwnode);
365+
}
366+
367+
#ifdef CONFIG_ACPI
368+
369+
/*
370+
* On ACPI based systems, PCI enumeration happens early during boot in
371+
* acpi_scan_init(). PCI enumeration expects MSI domain setup before
372+
* it calls pci_set_msi_domain(). Hence, unlike in DT where
373+
* imsic-platform drive probe happens late during boot, ACPI based
374+
* systems need to setup the MSI domain early.
375+
*/
376+
int imsic_platform_acpi_probe(struct fwnode_handle *fwnode)
377+
{
378+
return imsic_platform_probe_common(fwnode);
379+
}
380+
381+
#endif
382+
363383
static const struct of_device_id imsic_platform_match[] = {
364384
{ .compatible = "riscv,imsics" },
365385
{}
@@ -370,6 +390,6 @@ static struct platform_driver imsic_platform_driver = {
370390
.name = "riscv-imsic",
371391
.of_match_table = imsic_platform_match,
372392
},
373-
.probe = imsic_platform_probe,
393+
.probe = imsic_platform_dt_probe,
374394
};
375395
builtin_platform_driver(imsic_platform_driver);

drivers/irqchip/irq-riscv-imsic-state.c

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
#define pr_fmt(fmt) "riscv-imsic: " fmt
8+
#include <linux/acpi.h>
89
#include <linux/cpu.h>
910
#include <linux/bitmap.h>
1011
#include <linux/interrupt.h>
@@ -564,18 +565,36 @@ static int __init imsic_populate_global_dt(struct fwnode_handle *fwnode,
564565
return 0;
565566
}
566567

568+
static int __init imsic_populate_global_acpi(struct fwnode_handle *fwnode,
569+
struct imsic_global_config *global,
570+
u32 *nr_parent_irqs, void *opaque)
571+
{
572+
struct acpi_madt_imsic *imsic = (struct acpi_madt_imsic *)opaque;
573+
574+
global->guest_index_bits = imsic->guest_index_bits;
575+
global->hart_index_bits = imsic->hart_index_bits;
576+
global->group_index_bits = imsic->group_index_bits;
577+
global->group_index_shift = imsic->group_index_shift;
578+
global->nr_ids = imsic->num_ids;
579+
global->nr_guest_ids = imsic->num_guest_ids;
580+
return 0;
581+
}
582+
567583
static int __init imsic_get_parent_hartid(struct fwnode_handle *fwnode,
568584
u32 index, unsigned long *hartid)
569585
{
570586
struct of_phandle_args parent;
571587
int rc;
572588

573-
/*
574-
* Currently, only OF fwnode is supported so extend this
575-
* function for ACPI support.
576-
*/
577-
if (!is_of_node(fwnode))
578-
return -EINVAL;
589+
if (!is_of_node(fwnode)) {
590+
if (hartid)
591+
*hartid = acpi_rintc_index_to_hartid(index);
592+
593+
if (!hartid || (*hartid == INVALID_HARTID))
594+
return -EINVAL;
595+
596+
return 0;
597+
}
579598

580599
rc = of_irq_parse_one(to_of_node(fwnode), index, &parent);
581600
if (rc)
@@ -594,33 +613,23 @@ static int __init imsic_get_parent_hartid(struct fwnode_handle *fwnode,
594613
static int __init imsic_get_mmio_resource(struct fwnode_handle *fwnode,
595614
u32 index, struct resource *res)
596615
{
597-
/*
598-
* Currently, only OF fwnode is supported so extend this
599-
* function for ACPI support.
600-
*/
601616
if (!is_of_node(fwnode))
602-
return -EINVAL;
617+
return acpi_rintc_get_imsic_mmio_info(index, res);
603618

604619
return of_address_to_resource(to_of_node(fwnode), index, res);
605620
}
606621

607622
static int __init imsic_parse_fwnode(struct fwnode_handle *fwnode,
608623
struct imsic_global_config *global,
609624
u32 *nr_parent_irqs,
610-
u32 *nr_mmios)
625+
u32 *nr_mmios,
626+
void *opaque)
611627
{
612628
unsigned long hartid;
613629
struct resource res;
614630
int rc;
615631
u32 i;
616632

617-
/*
618-
* Currently, only OF fwnode is supported so extend this
619-
* function for ACPI support.
620-
*/
621-
if (!is_of_node(fwnode))
622-
return -EINVAL;
623-
624633
*nr_parent_irqs = 0;
625634
*nr_mmios = 0;
626635

@@ -632,7 +641,11 @@ static int __init imsic_parse_fwnode(struct fwnode_handle *fwnode,
632641
return -EINVAL;
633642
}
634643

635-
rc = imsic_populate_global_dt(fwnode, global, nr_parent_irqs);
644+
if (is_of_node(fwnode))
645+
rc = imsic_populate_global_dt(fwnode, global, nr_parent_irqs);
646+
else
647+
rc = imsic_populate_global_acpi(fwnode, global, nr_parent_irqs, opaque);
648+
636649
if (rc)
637650
return rc;
638651

@@ -701,7 +714,7 @@ static int __init imsic_parse_fwnode(struct fwnode_handle *fwnode,
701714
return 0;
702715
}
703716

704-
int __init imsic_setup_state(struct fwnode_handle *fwnode)
717+
int __init imsic_setup_state(struct fwnode_handle *fwnode, void *opaque)
705718
{
706719
u32 i, j, index, nr_parent_irqs, nr_mmios, nr_handlers = 0;
707720
struct imsic_global_config *global;
@@ -742,7 +755,7 @@ int __init imsic_setup_state(struct fwnode_handle *fwnode)
742755
}
743756

744757
/* Parse IMSIC fwnode */
745-
rc = imsic_parse_fwnode(fwnode, global, &nr_parent_irqs, &nr_mmios);
758+
rc = imsic_parse_fwnode(fwnode, global, &nr_parent_irqs, &nr_mmios, opaque);
746759
if (rc)
747760
goto out_free_local;
748761

drivers/irqchip/irq-riscv-imsic-state.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ void imsic_vector_debug_show_summary(struct seq_file *m, int ind);
102102

103103
void imsic_state_online(void);
104104
void imsic_state_offline(void);
105-
int imsic_setup_state(struct fwnode_handle *fwnode);
105+
int imsic_setup_state(struct fwnode_handle *fwnode, void *opaque);
106106
int imsic_irqdomain_init(void);
107107

108108
#endif

include/linux/irqchip/riscv-imsic.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
#include <linux/types.h>
1010
#include <linux/bitops.h>
11+
#include <linux/device.h>
12+
#include <linux/fwnode.h>
1113
#include <asm/csr.h>
1214

1315
#define IMSIC_MMIO_PAGE_SHIFT 12
@@ -84,4 +86,11 @@ static inline const struct imsic_global_config *imsic_get_global_config(void)
8486

8587
#endif
8688

89+
#ifdef CONFIG_ACPI
90+
int imsic_platform_acpi_probe(struct fwnode_handle *fwnode);
91+
struct fwnode_handle *imsic_acpi_get_fwnode(struct device *dev);
92+
#else
93+
static inline struct fwnode_handle *imsic_acpi_get_fwnode(struct device *dev) { return NULL; }
94+
#endif
95+
8796
#endif

0 commit comments

Comments
 (0)