diff options
Diffstat (limited to 'drivers/irqchip/irq-riscv-imsic-early.c')
| -rw-r--r-- | drivers/irqchip/irq-riscv-imsic-early.c | 45 |
1 files changed, 32 insertions, 13 deletions
diff --git a/drivers/irqchip/irq-riscv-imsic-early.c b/drivers/irqchip/irq-riscv-imsic-early.c index c5c2e6929a2f..6bac67cc0b6d 100644 --- a/drivers/irqchip/irq-riscv-imsic-early.c +++ b/drivers/irqchip/irq-riscv-imsic-early.c @@ -7,7 +7,9 @@ #define pr_fmt(fmt) "riscv-imsic: " fmt #include <linux/acpi.h> #include <linux/cpu.h> +#include <linux/export.h> #include <linux/interrupt.h> +#include <linux/init.h> #include <linux/io.h> #include <linux/irq.h> #include <linux/irqchip.h> @@ -21,23 +23,37 @@ #include "irq-riscv-imsic-state.h" static int imsic_parent_irq; +bool imsic_noipi __ro_after_init; + +static int __init imsic_noipi_cfg(char *buf) +{ + imsic_noipi = true; + return 0; +} +early_param("irqchip.riscv_imsic_noipi", imsic_noipi_cfg); #ifdef CONFIG_SMP static void imsic_ipi_send(unsigned int cpu) { struct imsic_local_config *local = per_cpu_ptr(imsic->global.local, cpu); - writel_relaxed(IMSIC_IPI_ID, local->msi_va); + writel(IMSIC_IPI_ID, local->msi_va); } static void imsic_ipi_starting_cpu(void) { + if (imsic_noipi) + return; + /* Enable IPIs for current CPU. */ __imsic_id_set_enable(IMSIC_IPI_ID); } static void imsic_ipi_dying_cpu(void) { + if (imsic_noipi) + return; + /* Disable IPIs for current CPU. */ __imsic_id_clear_enable(IMSIC_IPI_ID); } @@ -46,6 +62,9 @@ static int __init imsic_ipi_domain_init(void) { int virq; + if (imsic_noipi) + return 0; + /* Create IMSIC IPI multiplexing */ virq = ipi_mux_create(IMSIC_NR_IPI, imsic_ipi_send); if (virq <= 0) @@ -72,34 +91,33 @@ static int __init imsic_ipi_domain_init(void) { return 0; } */ static void imsic_handle_irq(struct irq_desc *desc) { + struct imsic_local_priv *lpriv = this_cpu_ptr(imsic->lpriv); struct irq_chip *chip = irq_desc_get_chip(desc); - int err, cpu = smp_processor_id(); - struct imsic_vector *vec; unsigned long local_id; + /* + * Process pending local synchronization instead of waiting + * for per-CPU local timer to expire. + */ + imsic_local_sync_all(false); + chained_irq_enter(chip, desc); while ((local_id = csr_swap(CSR_TOPEI, 0))) { local_id >>= TOPEI_ID_SHIFT; - if (local_id == IMSIC_IPI_ID) { + if (!imsic_noipi && local_id == IMSIC_IPI_ID) { if (IS_ENABLED(CONFIG_SMP)) ipi_mux_process(); continue; } - if (unlikely(!imsic->base_domain)) - continue; - - vec = imsic_vector_from_local_id(cpu, local_id); - if (!vec) { + if (unlikely(local_id > imsic->global.nr_ids)) { pr_warn_ratelimited("vector not found for local ID 0x%lx\n", local_id); continue; } - err = generic_handle_domain_irq(imsic->base_domain, vec->hwirq); - if (unlikely(err)) - pr_warn_ratelimited("hwirq 0x%x mapping not found\n", vec->hwirq); + generic_handle_irq(lpriv->vectors[local_id].irq); } chained_irq_exit(chip, desc); @@ -120,7 +138,7 @@ static int imsic_starting_cpu(unsigned int cpu) * Interrupts identities might have been enabled/disabled while * this CPU was not running so sync-up local enable/disable state. */ - imsic_local_sync_all(); + imsic_local_sync_all(true); /* Enable local interrupt delivery */ imsic_local_delivery(true); @@ -211,6 +229,7 @@ struct fwnode_handle *imsic_acpi_get_fwnode(struct device *dev) { return imsic_acpi_fwnode; } +EXPORT_SYMBOL_GPL(imsic_acpi_get_fwnode); static int __init imsic_early_acpi_init(union acpi_subtable_headers *header, const unsigned long end) |
