0001
0002
0003
0004
0005
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
0034
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
0047
0048
0049
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
0109
0110
0111
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);