irqchip/sifive-plic: Add ACPI support

Add ACPI support in PLIC driver. Use the mapping created early during
boot to get details about the PLIC.

Signed-off-by: Sunil V L <sunilvl@ventanamicro.com>
Co-developed-by: Haibo Xu <haibo1.xu@intel.com>
Signed-off-by: Haibo Xu <haibo1.xu@intel.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
Tested-by: Björn Töpel <bjorn@rivosinc.com>
Acked-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://patch.msgid.link/20240812005929.113499-18-sunilvl@ventanamicro.com
[ rjw: Rebase on top of recent irqchip changes ]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
Sunil V L 2024-08-27 19:51:35 +02:00 committed by Rafael J. Wysocki
parent 5122e380c2
commit 206dd13a10

View File

@ -4,6 +4,7 @@
* Copyright (C) 2018 Christoph Hellwig
*/
#define pr_fmt(fmt) "riscv-plic: " fmt
#include <linux/acpi.h>
#include <linux/cpu.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@ -71,6 +72,8 @@ struct plic_priv {
unsigned long plic_quirks;
unsigned int nr_irqs;
unsigned long *prio_save;
u32 gsi_base;
int acpi_plic_id;
};
struct plic_handler {
@ -325,6 +328,10 @@ static int plic_irq_domain_translate(struct irq_domain *d,
{
struct plic_priv *priv = d->host_data;
/* For DT, gsi_base is always zero. */
if (fwspec->param[0] >= priv->gsi_base)
fwspec->param[0] = fwspec->param[0] - priv->gsi_base;
if (test_bit(PLIC_QUIRK_EDGE_INTERRUPT, &priv->plic_quirks))
return irq_domain_translate_twocell(d, fwspec, hwirq, type);
@ -426,17 +433,36 @@ static const struct of_device_id plic_match[] = {
{}
};
#ifdef CONFIG_ACPI
static const struct acpi_device_id plic_acpi_match[] = {
{ "RSCV0001", 0 },
{}
};
MODULE_DEVICE_TABLE(acpi, plic_acpi_match);
#endif
static int plic_parse_nr_irqs_and_contexts(struct fwnode_handle *fwnode,
u32 *nr_irqs, u32 *nr_contexts)
u32 *nr_irqs, u32 *nr_contexts,
u32 *gsi_base, u32 *id)
{
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)) {
rc = riscv_acpi_get_gsi_info(fwnode, gsi_base, id, nr_irqs, NULL);
if (rc) {
pr_err("%pfwP: failed to find GSI mapping\n", fwnode);
return rc;
}
*nr_contexts = acpi_rintc_get_plic_nr_contexts(*id);
if (WARN_ON(!*nr_contexts)) {
pr_err("%pfwP: no PLIC context available\n", fwnode);
return -EINVAL;
}
return 0;
}
rc = of_property_read_u32(to_of_node(fwnode), "riscv,ndev", nr_irqs);
if (rc) {
@ -450,22 +476,28 @@ static int plic_parse_nr_irqs_and_contexts(struct fwnode_handle *fwnode,
return -EINVAL;
}
*gsi_base = 0;
*id = 0;
return 0;
}
static int plic_parse_context_parent(struct fwnode_handle *fwnode, u32 context,
u32 *parent_hwirq, int *parent_cpu)
u32 *parent_hwirq, int *parent_cpu, u32 id)
{
struct of_phandle_args parent;
unsigned long hartid;
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)) {
hartid = acpi_rintc_ext_parent_to_hartid(id, context);
if (hartid == INVALID_HARTID)
return -EINVAL;
*parent_cpu = riscv_hartid_to_cpuid(hartid);
*parent_hwirq = RV_IRQ_EXT;
return 0;
}
rc = of_irq_parse_one(to_of_node(fwnode), context, &parent);
if (rc)
@ -489,6 +521,8 @@ static int plic_probe(struct fwnode_handle *fwnode)
struct plic_priv *priv;
irq_hw_number_t hwirq;
void __iomem *regs;
int id, context_id;
u32 gsi_base;
if (is_of_node(fwnode)) {
const struct of_device_id *id;
@ -501,10 +535,12 @@ static int plic_probe(struct fwnode_handle *fwnode)
if (!regs)
return -ENOMEM;
} else {
return -ENODEV;
regs = devm_platform_ioremap_resource(to_platform_device(fwnode->dev), 0);
if (IS_ERR(regs))
return PTR_ERR(regs);
}
error = plic_parse_nr_irqs_and_contexts(fwnode, &nr_irqs, &nr_contexts);
error = plic_parse_nr_irqs_and_contexts(fwnode, &nr_irqs, &nr_contexts, &gsi_base, &id);
if (error)
goto fail_free_regs;
@ -518,6 +554,8 @@ static int plic_probe(struct fwnode_handle *fwnode)
priv->plic_quirks = plic_quirks;
priv->nr_irqs = nr_irqs;
priv->regs = regs;
priv->gsi_base = gsi_base;
priv->acpi_plic_id = id;
priv->prio_save = bitmap_zalloc(nr_irqs, GFP_KERNEL);
if (!priv->prio_save) {
@ -526,12 +564,23 @@ static int plic_probe(struct fwnode_handle *fwnode)
}
for (i = 0; i < nr_contexts; i++) {
error = plic_parse_context_parent(fwnode, i, &parent_hwirq, &cpu);
error = plic_parse_context_parent(fwnode, i, &parent_hwirq, &cpu,
priv->acpi_plic_id);
if (error) {
pr_warn("%pfwP: hwirq for context%d not found\n", fwnode, i);
continue;
}
if (is_of_node(fwnode)) {
context_id = i;
} else {
context_id = acpi_rintc_get_plic_context(priv->acpi_plic_id, i);
if (context_id == INVALID_CONTEXT) {
pr_warn("%pfwP: invalid context id for context%d\n", fwnode, i);
continue;
}
}
/*
* Skip contexts other than external interrupts for our
* privilege level.
@ -569,10 +618,10 @@ static int plic_probe(struct fwnode_handle *fwnode)
cpumask_set_cpu(cpu, &priv->lmask);
handler->present = true;
handler->hart_base = priv->regs + CONTEXT_BASE +
i * CONTEXT_SIZE;
context_id * CONTEXT_SIZE;
raw_spin_lock_init(&handler->enable_lock);
handler->enable_base = priv->regs + CONTEXT_ENABLE_BASE +
i * CONTEXT_ENABLE_SIZE;
context_id * CONTEXT_ENABLE_SIZE;
handler->priv = priv;
handler->enable_save = kcalloc(DIV_ROUND_UP(nr_irqs, 32),
@ -588,8 +637,8 @@ done:
nr_handlers++;
}
priv->irqdomain = irq_domain_add_linear(to_of_node(fwnode), nr_irqs + 1,
&plic_irqdomain_ops, priv);
priv->irqdomain = irq_domain_create_linear(fwnode, nr_irqs + 1,
&plic_irqdomain_ops, priv);
if (WARN_ON(!priv->irqdomain))
goto fail_cleanup_contexts;
@ -626,13 +675,18 @@ done:
}
}
#ifdef CONFIG_ACPI
if (!acpi_disabled)
acpi_dev_clear_dependencies(ACPI_COMPANION(fwnode->dev));
#endif
pr_info("%pfwP: mapped %d interrupts with %d handlers for %d contexts.\n",
fwnode, nr_irqs, nr_handlers, nr_contexts);
return 0;
fail_cleanup_contexts:
for (i = 0; i < nr_contexts; i++) {
if (plic_parse_context_parent(fwnode, i, &parent_hwirq, &cpu))
if (plic_parse_context_parent(fwnode, i, &parent_hwirq, &cpu, priv->acpi_plic_id))
continue;
if (parent_hwirq != RV_IRQ_EXT || cpu < 0)
continue;
@ -663,6 +717,7 @@ static struct platform_driver plic_driver = {
.name = "riscv-plic",
.of_match_table = plic_match,
.suppress_bind_attrs = true,
.acpi_match_table = ACPI_PTR(plic_acpi_match),
},
.probe = plic_platform_probe,
};