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