Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2012 Regents of the University of California
0004  * Copyright (C) 2017-2018 SiFive
0005  * Copyright (C) 2020 Western Digital Corporation or its affiliates.
0006  */
0007 
0008 #define pr_fmt(fmt) "riscv-intc: " fmt
0009 #include <linux/atomic.h>
0010 #include <linux/bits.h>
0011 #include <linux/cpu.h>
0012 #include <linux/irq.h>
0013 #include <linux/irqchip.h>
0014 #include <linux/irqdomain.h>
0015 #include <linux/interrupt.h>
0016 #include <linux/module.h>
0017 #include <linux/of.h>
0018 #include <linux/smp.h>
0019 
0020 static struct irq_domain *intc_domain;
0021 
0022 static asmlinkage void riscv_intc_irq(struct pt_regs *regs)
0023 {
0024     unsigned long cause = regs->cause & ~CAUSE_IRQ_FLAG;
0025 
0026     if (unlikely(cause >= BITS_PER_LONG))
0027         panic("unexpected interrupt cause");
0028 
0029     switch (cause) {
0030 #ifdef CONFIG_SMP
0031     case RV_IRQ_SOFT:
0032         /*
0033          * We only use software interrupts to pass IPIs, so if a
0034          * non-SMP system gets one, then we don't know what to do.
0035          */
0036         handle_IPI(regs);
0037         break;
0038 #endif
0039     default:
0040         generic_handle_domain_irq(intc_domain, cause);
0041         break;
0042     }
0043 }
0044 
0045 /*
0046  * On RISC-V systems local interrupts are masked or unmasked by writing
0047  * the SIE (Supervisor Interrupt Enable) CSR.  As CSRs can only be written
0048  * on the local hart, these functions can only be called on the hart that
0049  * corresponds to the IRQ chip.
0050  */
0051 
0052 static void riscv_intc_irq_mask(struct irq_data *d)
0053 {
0054     csr_clear(CSR_IE, BIT(d->hwirq));
0055 }
0056 
0057 static void riscv_intc_irq_unmask(struct irq_data *d)
0058 {
0059     csr_set(CSR_IE, BIT(d->hwirq));
0060 }
0061 
0062 static int riscv_intc_cpu_starting(unsigned int cpu)
0063 {
0064     csr_set(CSR_IE, BIT(RV_IRQ_SOFT));
0065     return 0;
0066 }
0067 
0068 static int riscv_intc_cpu_dying(unsigned int cpu)
0069 {
0070     csr_clear(CSR_IE, BIT(RV_IRQ_SOFT));
0071     return 0;
0072 }
0073 
0074 static struct irq_chip riscv_intc_chip = {
0075     .name = "RISC-V INTC",
0076     .irq_mask = riscv_intc_irq_mask,
0077     .irq_unmask = riscv_intc_irq_unmask,
0078 };
0079 
0080 static int riscv_intc_domain_map(struct irq_domain *d, unsigned int irq,
0081                  irq_hw_number_t hwirq)
0082 {
0083     irq_set_percpu_devid(irq);
0084     irq_domain_set_info(d, irq, hwirq, &riscv_intc_chip, d->host_data,
0085                 handle_percpu_devid_irq, NULL, NULL);
0086 
0087     return 0;
0088 }
0089 
0090 static const struct irq_domain_ops riscv_intc_domain_ops = {
0091     .map    = riscv_intc_domain_map,
0092     .xlate  = irq_domain_xlate_onecell,
0093 };
0094 
0095 static int __init riscv_intc_init(struct device_node *node,
0096                   struct device_node *parent)
0097 {
0098     int rc;
0099     unsigned long hartid;
0100 
0101     rc = riscv_of_parent_hartid(node, &hartid);
0102     if (rc < 0) {
0103         pr_warn("unable to find hart id for %pOF\n", node);
0104         return 0;
0105     }
0106 
0107     /*
0108      * The DT will have one INTC DT node under each CPU (or HART)
0109      * DT node so riscv_intc_init() function will be called once
0110      * for each INTC DT node. We only need to do INTC initialization
0111      * for the INTC DT node belonging to boot CPU (or boot HART).
0112      */
0113     if (riscv_hartid_to_cpuid(hartid) != smp_processor_id())
0114         return 0;
0115 
0116     intc_domain = irq_domain_add_linear(node, BITS_PER_LONG,
0117                         &riscv_intc_domain_ops, NULL);
0118     if (!intc_domain) {
0119         pr_err("unable to add IRQ domain\n");
0120         return -ENXIO;
0121     }
0122 
0123     rc = set_handle_irq(&riscv_intc_irq);
0124     if (rc) {
0125         pr_err("failed to set irq handler\n");
0126         return rc;
0127     }
0128 
0129     cpuhp_setup_state(CPUHP_AP_IRQ_RISCV_STARTING,
0130               "irqchip/riscv/intc:starting",
0131               riscv_intc_cpu_starting,
0132               riscv_intc_cpu_dying);
0133 
0134     pr_info("%d local interrupts mapped\n", BITS_PER_LONG);
0135 
0136     return 0;
0137 }
0138 
0139 IRQCHIP_DECLARE(riscv, "riscv,cpu-intc", riscv_intc_init);