]> Git Repo - J-linux.git/blob - drivers/irqchip/irq-riscv-intc.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / irqchip / irq-riscv-intc.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2012 Regents of the University of California
4  * Copyright (C) 2017-2018 SiFive
5  * Copyright (C) 2020 Western Digital Corporation or its affiliates.
6  */
7
8 #define pr_fmt(fmt) "riscv-intc: " fmt
9 #include <linux/acpi.h>
10 #include <linux/atomic.h>
11 #include <linux/bits.h>
12 #include <linux/cpu.h>
13 #include <linux/irq.h>
14 #include <linux/irqchip.h>
15 #include <linux/irqdomain.h>
16 #include <linux/interrupt.h>
17 #include <linux/module.h>
18 #include <linux/of.h>
19 #include <linux/smp.h>
20 #include <linux/soc/andes/irq.h>
21
22 #include <asm/hwcap.h>
23
24 static struct irq_domain *intc_domain;
25 static unsigned int riscv_intc_nr_irqs __ro_after_init = BITS_PER_LONG;
26 static unsigned int riscv_intc_custom_base __ro_after_init = BITS_PER_LONG;
27 static unsigned int riscv_intc_custom_nr_irqs __ro_after_init;
28
29 static void riscv_intc_irq(struct pt_regs *regs)
30 {
31         unsigned long cause = regs->cause & ~CAUSE_IRQ_FLAG;
32
33         if (generic_handle_domain_irq(intc_domain, cause))
34                 pr_warn_ratelimited("Failed to handle interrupt (cause: %ld)\n", cause);
35 }
36
37 static void riscv_intc_aia_irq(struct pt_regs *regs)
38 {
39         unsigned long topi;
40
41         while ((topi = csr_read(CSR_TOPI)))
42                 generic_handle_domain_irq(intc_domain, topi >> TOPI_IID_SHIFT);
43 }
44
45 /*
46  * On RISC-V systems local interrupts are masked or unmasked by writing
47  * the SIE (Supervisor Interrupt Enable) CSR.  As CSRs can only be written
48  * on the local hart, these functions can only be called on the hart that
49  * corresponds to the IRQ chip.
50  */
51
52 static void riscv_intc_irq_mask(struct irq_data *d)
53 {
54         if (IS_ENABLED(CONFIG_32BIT) && d->hwirq >= BITS_PER_LONG)
55                 csr_clear(CSR_IEH, BIT(d->hwirq - BITS_PER_LONG));
56         else
57                 csr_clear(CSR_IE, BIT(d->hwirq));
58 }
59
60 static void riscv_intc_irq_unmask(struct irq_data *d)
61 {
62         if (IS_ENABLED(CONFIG_32BIT) && d->hwirq >= BITS_PER_LONG)
63                 csr_set(CSR_IEH, BIT(d->hwirq - BITS_PER_LONG));
64         else
65                 csr_set(CSR_IE, BIT(d->hwirq));
66 }
67
68 static void andes_intc_irq_mask(struct irq_data *d)
69 {
70         /*
71          * Andes specific S-mode local interrupt causes (hwirq)
72          * are defined as (256 + n) and controlled by n-th bit
73          * of SLIE.
74          */
75         unsigned int mask = BIT(d->hwirq % BITS_PER_LONG);
76
77         if (d->hwirq < ANDES_SLI_CAUSE_BASE)
78                 csr_clear(CSR_IE, mask);
79         else
80                 csr_clear(ANDES_CSR_SLIE, mask);
81 }
82
83 static void andes_intc_irq_unmask(struct irq_data *d)
84 {
85         unsigned int mask = BIT(d->hwirq % BITS_PER_LONG);
86
87         if (d->hwirq < ANDES_SLI_CAUSE_BASE)
88                 csr_set(CSR_IE, mask);
89         else
90                 csr_set(ANDES_CSR_SLIE, mask);
91 }
92
93 static void riscv_intc_irq_eoi(struct irq_data *d)
94 {
95         /*
96          * The RISC-V INTC driver uses handle_percpu_devid_irq() flow
97          * for the per-HART local interrupts and child irqchip drivers
98          * (such as PLIC, SBI IPI, CLINT, APLIC, IMSIC, etc) implement
99          * chained handlers for the per-HART local interrupts.
100          *
101          * In the absence of irq_eoi(), the chained_irq_enter() and
102          * chained_irq_exit() functions (used by child irqchip drivers)
103          * will do unnecessary mask/unmask of per-HART local interrupts
104          * at the time of handling interrupts. To avoid this, we provide
105          * an empty irq_eoi() callback for RISC-V INTC irqchip.
106          */
107 }
108
109 static struct irq_chip riscv_intc_chip = {
110         .name = "RISC-V INTC",
111         .irq_mask = riscv_intc_irq_mask,
112         .irq_unmask = riscv_intc_irq_unmask,
113         .irq_eoi = riscv_intc_irq_eoi,
114 };
115
116 static struct irq_chip andes_intc_chip = {
117         .name           = "RISC-V INTC",
118         .irq_mask       = andes_intc_irq_mask,
119         .irq_unmask     = andes_intc_irq_unmask,
120         .irq_eoi        = riscv_intc_irq_eoi,
121 };
122
123 static int riscv_intc_domain_map(struct irq_domain *d, unsigned int irq,
124                                  irq_hw_number_t hwirq)
125 {
126         struct irq_chip *chip = d->host_data;
127
128         irq_set_percpu_devid(irq);
129         irq_domain_set_info(d, irq, hwirq, chip, NULL, handle_percpu_devid_irq,
130                             NULL, NULL);
131
132         return 0;
133 }
134
135 static int riscv_intc_domain_alloc(struct irq_domain *domain,
136                                    unsigned int virq, unsigned int nr_irqs,
137                                    void *arg)
138 {
139         int i, ret;
140         irq_hw_number_t hwirq;
141         unsigned int type = IRQ_TYPE_NONE;
142         struct irq_fwspec *fwspec = arg;
143
144         ret = irq_domain_translate_onecell(domain, fwspec, &hwirq, &type);
145         if (ret)
146                 return ret;
147
148         /*
149          * Only allow hwirq for which we have corresponding standard or
150          * custom interrupt enable register.
151          */
152         if (hwirq >= riscv_intc_nr_irqs &&
153             (hwirq < riscv_intc_custom_base ||
154              hwirq >= riscv_intc_custom_base + riscv_intc_custom_nr_irqs))
155                 return -EINVAL;
156
157         for (i = 0; i < nr_irqs; i++) {
158                 ret = riscv_intc_domain_map(domain, virq + i, hwirq + i);
159                 if (ret)
160                         return ret;
161         }
162
163         return 0;
164 }
165
166 static const struct irq_domain_ops riscv_intc_domain_ops = {
167         .map    = riscv_intc_domain_map,
168         .xlate  = irq_domain_xlate_onecell,
169         .alloc  = riscv_intc_domain_alloc
170 };
171
172 static struct fwnode_handle *riscv_intc_hwnode(void)
173 {
174         return intc_domain->fwnode;
175 }
176
177 static int __init riscv_intc_init_common(struct fwnode_handle *fn, struct irq_chip *chip)
178 {
179         int rc;
180
181         intc_domain = irq_domain_create_tree(fn, &riscv_intc_domain_ops, chip);
182         if (!intc_domain) {
183                 pr_err("unable to add IRQ domain\n");
184                 return -ENXIO;
185         }
186
187         if (riscv_isa_extension_available(NULL, SxAIA)) {
188                 riscv_intc_nr_irqs = 64;
189                 rc = set_handle_irq(&riscv_intc_aia_irq);
190         } else {
191                 rc = set_handle_irq(&riscv_intc_irq);
192         }
193         if (rc) {
194                 pr_err("failed to set irq handler\n");
195                 return rc;
196         }
197
198         riscv_set_intc_hwnode_fn(riscv_intc_hwnode);
199
200         pr_info("%d local interrupts mapped%s\n",
201                 riscv_intc_nr_irqs,
202                 riscv_isa_extension_available(NULL, SxAIA) ? " using AIA" : "");
203         if (riscv_intc_custom_nr_irqs)
204                 pr_info("%d custom local interrupts mapped\n", riscv_intc_custom_nr_irqs);
205
206         return 0;
207 }
208
209 static int __init riscv_intc_init(struct device_node *node,
210                                   struct device_node *parent)
211 {
212         struct irq_chip *chip = &riscv_intc_chip;
213         unsigned long hartid;
214         int rc;
215
216         rc = riscv_of_parent_hartid(node, &hartid);
217         if (rc < 0) {
218                 pr_warn("unable to find hart id for %pOF\n", node);
219                 return 0;
220         }
221
222         /*
223          * The DT will have one INTC DT node under each CPU (or HART)
224          * DT node so riscv_intc_init() function will be called once
225          * for each INTC DT node. We only need to do INTC initialization
226          * for the INTC DT node belonging to boot CPU (or boot HART).
227          */
228         if (riscv_hartid_to_cpuid(hartid) != smp_processor_id()) {
229                 /*
230                  * The INTC nodes of each CPU are suppliers for downstream
231                  * interrupt controllers (such as PLIC, IMSIC and APLIC
232                  * direct-mode) so we should mark an INTC node as initialized
233                  * if we are not creating IRQ domain for it.
234                  */
235                 fwnode_dev_initialized(of_fwnode_handle(node), true);
236                 return 0;
237         }
238
239         if (of_device_is_compatible(node, "andestech,cpu-intc")) {
240                 riscv_intc_custom_base = ANDES_SLI_CAUSE_BASE;
241                 riscv_intc_custom_nr_irqs = ANDES_RV_IRQ_LAST;
242                 chip = &andes_intc_chip;
243         }
244
245         return riscv_intc_init_common(of_node_to_fwnode(node), chip);
246 }
247
248 IRQCHIP_DECLARE(riscv, "riscv,cpu-intc", riscv_intc_init);
249 IRQCHIP_DECLARE(andes, "andestech,cpu-intc", riscv_intc_init);
250
251 #ifdef CONFIG_ACPI
252
253 struct rintc_data {
254         union {
255                 u32             ext_intc_id;
256                 struct {
257                         u32     context_id      : 16,
258                                 reserved        :  8,
259                                 aplic_plic_id   :  8;
260                 };
261         };
262         unsigned long           hart_id;
263         u64                     imsic_addr;
264         u32                     imsic_size;
265 };
266
267 static u32 nr_rintc;
268 static struct rintc_data **rintc_acpi_data;
269
270 #define for_each_matching_plic(_plic_id)                                \
271         unsigned int _plic;                                             \
272                                                                         \
273         for (_plic = 0; _plic < nr_rintc; _plic++)                      \
274                 if (rintc_acpi_data[_plic]->aplic_plic_id != _plic_id)  \
275                         continue;                                       \
276                 else
277
278 unsigned int acpi_rintc_get_plic_nr_contexts(unsigned int plic_id)
279 {
280         unsigned int nctx = 0;
281
282         for_each_matching_plic(plic_id)
283                 nctx++;
284
285         return nctx;
286 }
287
288 static struct rintc_data *get_plic_context(unsigned int plic_id, unsigned int ctxt_idx)
289 {
290         unsigned int ctxt = 0;
291
292         for_each_matching_plic(plic_id) {
293                 if (ctxt == ctxt_idx)
294                         return rintc_acpi_data[_plic];
295
296                 ctxt++;
297         }
298
299         return NULL;
300 }
301
302 unsigned long acpi_rintc_ext_parent_to_hartid(unsigned int plic_id, unsigned int ctxt_idx)
303 {
304         struct rintc_data *data = get_plic_context(plic_id, ctxt_idx);
305
306         return data ? data->hart_id : INVALID_HARTID;
307 }
308
309 unsigned int acpi_rintc_get_plic_context(unsigned int plic_id, unsigned int ctxt_idx)
310 {
311         struct rintc_data *data = get_plic_context(plic_id, ctxt_idx);
312
313         return data ? data->context_id : INVALID_CONTEXT;
314 }
315
316 unsigned long acpi_rintc_index_to_hartid(u32 index)
317 {
318         return index >= nr_rintc ? INVALID_HARTID : rintc_acpi_data[index]->hart_id;
319 }
320
321 int acpi_rintc_get_imsic_mmio_info(u32 index, struct resource *res)
322 {
323         if (index >= nr_rintc)
324                 return -1;
325
326         res->start = rintc_acpi_data[index]->imsic_addr;
327         res->end = res->start + rintc_acpi_data[index]->imsic_size - 1;
328         res->flags = IORESOURCE_MEM;
329         return 0;
330 }
331
332 static int __init riscv_intc_acpi_match(union acpi_subtable_headers *header,
333                                         const unsigned long end)
334 {
335         return 0;
336 }
337
338 static int __init riscv_intc_acpi_init(union acpi_subtable_headers *header,
339                                        const unsigned long end)
340 {
341         struct acpi_madt_rintc *rintc;
342         struct fwnode_handle *fn;
343         int count;
344         int rc;
345
346         if (!rintc_acpi_data) {
347                 count = acpi_table_parse_madt(ACPI_MADT_TYPE_RINTC, riscv_intc_acpi_match, 0);
348                 if (count <= 0)
349                         return -EINVAL;
350
351                 rintc_acpi_data = kcalloc(count, sizeof(*rintc_acpi_data), GFP_KERNEL);
352                 if (!rintc_acpi_data)
353                         return -ENOMEM;
354         }
355
356         rintc = (struct acpi_madt_rintc *)header;
357         rintc_acpi_data[nr_rintc] = kzalloc(sizeof(*rintc_acpi_data[0]), GFP_KERNEL);
358         if (!rintc_acpi_data[nr_rintc])
359                 return -ENOMEM;
360
361         rintc_acpi_data[nr_rintc]->ext_intc_id = rintc->ext_intc_id;
362         rintc_acpi_data[nr_rintc]->hart_id = rintc->hart_id;
363         rintc_acpi_data[nr_rintc]->imsic_addr = rintc->imsic_addr;
364         rintc_acpi_data[nr_rintc]->imsic_size = rintc->imsic_size;
365         nr_rintc++;
366
367         /*
368          * The ACPI MADT will have one INTC for each CPU (or HART)
369          * so riscv_intc_acpi_init() function will be called once
370          * for each INTC. We only do INTC initialization
371          * for the INTC belonging to the boot CPU (or boot HART).
372          */
373         if (riscv_hartid_to_cpuid(rintc->hart_id) != smp_processor_id())
374                 return 0;
375
376         fn = irq_domain_alloc_named_fwnode("RISCV-INTC");
377         if (!fn) {
378                 pr_err("unable to allocate INTC FW node\n");
379                 return -ENOMEM;
380         }
381
382         rc = riscv_intc_init_common(fn, &riscv_intc_chip);
383         if (rc)
384                 irq_domain_free_fwnode(fn);
385         else
386                 acpi_set_irq_model(ACPI_IRQ_MODEL_RINTC, riscv_acpi_get_gsi_domain_id);
387
388         return rc;
389 }
390
391 IRQCHIP_ACPI_DECLARE(riscv_intc, ACPI_MADT_TYPE_RINTC, NULL,
392                      ACPI_MADT_RINTC_VERSION_V1, riscv_intc_acpi_init);
393 #endif
This page took 0.048112 seconds and 4 git commands to generate.