162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2012 Regents of the University of California 462306a36Sopenharmony_ci * Copyright (C) 2017-2018 SiFive 562306a36Sopenharmony_ci * Copyright (C) 2020 Western Digital Corporation or its affiliates. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) "riscv-intc: " fmt 962306a36Sopenharmony_ci#include <linux/acpi.h> 1062306a36Sopenharmony_ci#include <linux/atomic.h> 1162306a36Sopenharmony_ci#include <linux/bits.h> 1262306a36Sopenharmony_ci#include <linux/cpu.h> 1362306a36Sopenharmony_ci#include <linux/irq.h> 1462306a36Sopenharmony_ci#include <linux/irqchip.h> 1562306a36Sopenharmony_ci#include <linux/irqdomain.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/smp.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic struct irq_domain *intc_domain; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic asmlinkage void riscv_intc_irq(struct pt_regs *regs) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci unsigned long cause = regs->cause & ~CAUSE_IRQ_FLAG; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci if (unlikely(cause >= BITS_PER_LONG)) 2862306a36Sopenharmony_ci panic("unexpected interrupt cause"); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci generic_handle_domain_irq(intc_domain, cause); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * On RISC-V systems local interrupts are masked or unmasked by writing 3562306a36Sopenharmony_ci * the SIE (Supervisor Interrupt Enable) CSR. As CSRs can only be written 3662306a36Sopenharmony_ci * on the local hart, these functions can only be called on the hart that 3762306a36Sopenharmony_ci * corresponds to the IRQ chip. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic void riscv_intc_irq_mask(struct irq_data *d) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci csr_clear(CSR_IE, BIT(d->hwirq)); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic void riscv_intc_irq_unmask(struct irq_data *d) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci csr_set(CSR_IE, BIT(d->hwirq)); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic void riscv_intc_irq_eoi(struct irq_data *d) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci /* 5362306a36Sopenharmony_ci * The RISC-V INTC driver uses handle_percpu_devid_irq() flow 5462306a36Sopenharmony_ci * for the per-HART local interrupts and child irqchip drivers 5562306a36Sopenharmony_ci * (such as PLIC, SBI IPI, CLINT, APLIC, IMSIC, etc) implement 5662306a36Sopenharmony_ci * chained handlers for the per-HART local interrupts. 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * In the absence of irq_eoi(), the chained_irq_enter() and 5962306a36Sopenharmony_ci * chained_irq_exit() functions (used by child irqchip drivers) 6062306a36Sopenharmony_ci * will do unnecessary mask/unmask of per-HART local interrupts 6162306a36Sopenharmony_ci * at the time of handling interrupts. To avoid this, we provide 6262306a36Sopenharmony_ci * an empty irq_eoi() callback for RISC-V INTC irqchip. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic struct irq_chip riscv_intc_chip = { 6762306a36Sopenharmony_ci .name = "RISC-V INTC", 6862306a36Sopenharmony_ci .irq_mask = riscv_intc_irq_mask, 6962306a36Sopenharmony_ci .irq_unmask = riscv_intc_irq_unmask, 7062306a36Sopenharmony_ci .irq_eoi = riscv_intc_irq_eoi, 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int riscv_intc_domain_map(struct irq_domain *d, unsigned int irq, 7462306a36Sopenharmony_ci irq_hw_number_t hwirq) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci irq_set_percpu_devid(irq); 7762306a36Sopenharmony_ci irq_domain_set_info(d, irq, hwirq, &riscv_intc_chip, d->host_data, 7862306a36Sopenharmony_ci handle_percpu_devid_irq, NULL, NULL); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return 0; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int riscv_intc_domain_alloc(struct irq_domain *domain, 8462306a36Sopenharmony_ci unsigned int virq, unsigned int nr_irqs, 8562306a36Sopenharmony_ci void *arg) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci int i, ret; 8862306a36Sopenharmony_ci irq_hw_number_t hwirq; 8962306a36Sopenharmony_ci unsigned int type = IRQ_TYPE_NONE; 9062306a36Sopenharmony_ci struct irq_fwspec *fwspec = arg; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci ret = irq_domain_translate_onecell(domain, fwspec, &hwirq, &type); 9362306a36Sopenharmony_ci if (ret) 9462306a36Sopenharmony_ci return ret; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci for (i = 0; i < nr_irqs; i++) { 9762306a36Sopenharmony_ci ret = riscv_intc_domain_map(domain, virq + i, hwirq + i); 9862306a36Sopenharmony_ci if (ret) 9962306a36Sopenharmony_ci return ret; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic const struct irq_domain_ops riscv_intc_domain_ops = { 10662306a36Sopenharmony_ci .map = riscv_intc_domain_map, 10762306a36Sopenharmony_ci .xlate = irq_domain_xlate_onecell, 10862306a36Sopenharmony_ci .alloc = riscv_intc_domain_alloc 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic struct fwnode_handle *riscv_intc_hwnode(void) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci return intc_domain->fwnode; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic int __init riscv_intc_init_common(struct fwnode_handle *fn) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci int rc; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci intc_domain = irq_domain_create_linear(fn, BITS_PER_LONG, 12162306a36Sopenharmony_ci &riscv_intc_domain_ops, NULL); 12262306a36Sopenharmony_ci if (!intc_domain) { 12362306a36Sopenharmony_ci pr_err("unable to add IRQ domain\n"); 12462306a36Sopenharmony_ci return -ENXIO; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci rc = set_handle_irq(&riscv_intc_irq); 12862306a36Sopenharmony_ci if (rc) { 12962306a36Sopenharmony_ci pr_err("failed to set irq handler\n"); 13062306a36Sopenharmony_ci return rc; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci riscv_set_intc_hwnode_fn(riscv_intc_hwnode); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci pr_info("%d local interrupts mapped\n", BITS_PER_LONG); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int __init riscv_intc_init(struct device_node *node, 14162306a36Sopenharmony_ci struct device_node *parent) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci int rc; 14462306a36Sopenharmony_ci unsigned long hartid; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci rc = riscv_of_parent_hartid(node, &hartid); 14762306a36Sopenharmony_ci if (rc < 0) { 14862306a36Sopenharmony_ci pr_warn("unable to find hart id for %pOF\n", node); 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* 15362306a36Sopenharmony_ci * The DT will have one INTC DT node under each CPU (or HART) 15462306a36Sopenharmony_ci * DT node so riscv_intc_init() function will be called once 15562306a36Sopenharmony_ci * for each INTC DT node. We only need to do INTC initialization 15662306a36Sopenharmony_ci * for the INTC DT node belonging to boot CPU (or boot HART). 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci if (riscv_hartid_to_cpuid(hartid) != smp_processor_id()) { 15962306a36Sopenharmony_ci /* 16062306a36Sopenharmony_ci * The INTC nodes of each CPU are suppliers for downstream 16162306a36Sopenharmony_ci * interrupt controllers (such as PLIC, IMSIC and APLIC 16262306a36Sopenharmony_ci * direct-mode) so we should mark an INTC node as initialized 16362306a36Sopenharmony_ci * if we are not creating IRQ domain for it. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci fwnode_dev_initialized(of_fwnode_handle(node), true); 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return riscv_intc_init_common(of_node_to_fwnode(node)); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ciIRQCHIP_DECLARE(riscv, "riscv,cpu-intc", riscv_intc_init); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci#ifdef CONFIG_ACPI 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int __init riscv_intc_acpi_init(union acpi_subtable_headers *header, 17762306a36Sopenharmony_ci const unsigned long end) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct fwnode_handle *fn; 18062306a36Sopenharmony_ci struct acpi_madt_rintc *rintc; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci rintc = (struct acpi_madt_rintc *)header; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* 18562306a36Sopenharmony_ci * The ACPI MADT will have one INTC for each CPU (or HART) 18662306a36Sopenharmony_ci * so riscv_intc_acpi_init() function will be called once 18762306a36Sopenharmony_ci * for each INTC. We only do INTC initialization 18862306a36Sopenharmony_ci * for the INTC belonging to the boot CPU (or boot HART). 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_ci if (riscv_hartid_to_cpuid(rintc->hart_id) != smp_processor_id()) 19162306a36Sopenharmony_ci return 0; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci fn = irq_domain_alloc_named_fwnode("RISCV-INTC"); 19462306a36Sopenharmony_ci if (!fn) { 19562306a36Sopenharmony_ci pr_err("unable to allocate INTC FW node\n"); 19662306a36Sopenharmony_ci return -ENOMEM; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return riscv_intc_init_common(fn); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ciIRQCHIP_ACPI_DECLARE(riscv_intc, ACPI_MADT_TYPE_RINTC, NULL, 20362306a36Sopenharmony_ci ACPI_MADT_RINTC_VERSION_V1, riscv_intc_acpi_init); 20462306a36Sopenharmony_ci#endif 205