1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2020 Loongson Technology Corporation Limited
4 */
5 #include <linux/compiler.h>
6 #include <linux/delay.h>
7 #include <linux/init.h>
8 #include <linux/interrupt.h>
9 #include <linux/irqchip.h>
10 #include <linux/irqdomain.h>
11 #include <linux/mm.h>
12 #include <linux/module.h>
13 #include <linux/stddef.h>
14 #include <asm/irq.h>
15 #include <asm/numa.h>
16 #include <asm/setup.h>
17 #include <asm/loongarchregs.h>
18 #include <loongson.h>
19
20 struct acpi_madt_lio_pic *acpi_liointc;
21 struct acpi_madt_eio_pic *acpi_eiointc[MAX_IO_PICS];
22
23 struct acpi_madt_ht_pic *acpi_htintc;
24 struct acpi_madt_lpc_pic *acpi_pchlpc;
25 struct acpi_madt_msi_pic *acpi_pchmsi[MAX_IO_PICS];
26 struct acpi_madt_bio_pic *acpi_pchpic[MAX_IO_PICS];
27
28 struct irq_domain *cpu_domain;
29 struct irq_domain *liointc_domain;
30 struct irq_domain *pch_lpc_domain;
31 struct irq_domain *pch_msi_domain[MAX_IO_PICS];
32 struct irq_domain *pch_pic_domain[MAX_IO_PICS];
33
find_pch_pic(u32 gsi)34 int find_pch_pic(u32 gsi)
35 {
36 int i, start, end;
37
38 /* Find the PCH_PIC that manages this GSI. */
39 for(i = 0; i < loongson_sysconf.nr_io_pics; i++) {
40 struct acpi_madt_bio_pic *irq_cfg = acpi_pchpic[i];
41
42 start = irq_cfg->gsi_base;
43 end = irq_cfg->gsi_base + irq_cfg->size;
44 if (gsi >= start && gsi < end)
45 return i;
46 }
47
48 printk(KERN_ERR "ERROR: Unable to locate PCH_PIC for GSI %d\n", gsi);
49 return -1;
50 }
51
setup_IRQ(void)52 void __init setup_IRQ(void)
53 {
54 int i;
55 struct irq_domain *parent_domain;
56
57 if (!acpi_eiointc[0])
58 cpu_data[0].options &= ~LOONGARCH_CPU_EXTIOI;
59
60 cpu_domain = loongarch_cpu_irq_init();
61 liointc_domain = liointc_acpi_init(cpu_domain, acpi_liointc);
62
63 if (cpu_has_extioi) {
64 pr_info("Using EIOINTC interrupt mode\n");
65 for(i = 0; i < loongson_sysconf.nr_io_pics; i++) {
66 parent_domain = eiointc_acpi_init(cpu_domain, acpi_eiointc[i]);
67 pch_pic_domain[i] = pch_pic_acpi_init(parent_domain, acpi_pchpic[i]);
68 pch_msi_domain[i] = pch_msi_acpi_init(parent_domain, acpi_pchmsi[i]);
69 }
70 } else {
71 pr_info("Using HTVECINTC interrupt mode\n");
72 parent_domain = htvec_acpi_init(liointc_domain, acpi_htintc);
73 pch_pic_domain[0] = pch_pic_acpi_init(parent_domain, acpi_pchpic[0]);
74 pch_msi_domain[0] = pch_msi_acpi_init(parent_domain, acpi_pchmsi[0]);
75 }
76
77 irq_set_default_host(pch_pic_domain[0]);
78 pch_lpc_domain = pch_lpc_acpi_init(pch_pic_domain[0], acpi_pchlpc);
79 }
80
arch_init_irq(void)81 void __init arch_init_irq(void)
82 {
83 int r, ipi_irq;
84 static int ipi_dummy_dev;
85
86 clear_csr_ecfg(ECFG0_IM);
87 clear_csr_estat(ESTATF_IP);
88
89 setup_IRQ();
90 #ifdef CONFIG_SMP
91 ipi_irq = get_ipi_irq();
92 irq_set_percpu_devid(ipi_irq);
93 r = request_percpu_irq(ipi_irq, loongson3_ipi_interrupt, "IPI", &ipi_dummy_dev);
94 if (r < 0)
95 panic("IPI IRQ request failed\n");
96 #endif
97
98 set_csr_ecfg(ECFGF_IP0 | ECFGF_IP1 | ECFGF_IP2 | ECFGF_IPI | ECFGF_PMC);
99 }
100