Commit | Line | Data |
---|---|---|
b2d3e335 HC |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (C) 2020-2022 Loongson Technology Corporation Limited | |
4 | */ | |
5 | ||
6 | #include <linux/init.h> | |
7 | #include <linux/kernel.h> | |
8 | #include <linux/interrupt.h> | |
9 | #include <linux/irq.h> | |
10 | #include <linux/irqchip.h> | |
11 | #include <linux/irqdomain.h> | |
12 | ||
13 | #include <asm/loongarch.h> | |
14 | #include <asm/setup.h> | |
15 | ||
16 | static struct irq_domain *irq_domain; | |
17 | struct fwnode_handle *cpuintc_handle; | |
18 | ||
e8bba72b JL |
19 | static u32 lpic_gsi_to_irq(u32 gsi) |
20 | { | |
21 | /* Only pch irqdomain transferring is required for LoongArch. */ | |
22 | if (gsi >= GSI_MIN_PCH_IRQ && gsi <= GSI_MAX_PCH_IRQ) | |
23 | return acpi_register_gsi(NULL, gsi, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_HIGH); | |
24 | ||
25 | return 0; | |
26 | } | |
27 | ||
28 | static struct fwnode_handle *lpic_get_gsi_domain_id(u32 gsi) | |
29 | { | |
30 | int id; | |
31 | struct fwnode_handle *domain_handle = NULL; | |
32 | ||
33 | switch (gsi) { | |
34 | case GSI_MIN_CPU_IRQ ... GSI_MAX_CPU_IRQ: | |
35 | if (liointc_handle) | |
36 | domain_handle = liointc_handle; | |
37 | break; | |
38 | ||
39 | case GSI_MIN_LPC_IRQ ... GSI_MAX_LPC_IRQ: | |
40 | if (pch_lpc_handle) | |
41 | domain_handle = pch_lpc_handle; | |
42 | break; | |
43 | ||
44 | case GSI_MIN_PCH_IRQ ... GSI_MAX_PCH_IRQ: | |
45 | id = find_pch_pic(gsi); | |
46 | if (id >= 0 && pch_pic_handle[id]) | |
47 | domain_handle = pch_pic_handle[id]; | |
48 | break; | |
49 | } | |
50 | ||
51 | return domain_handle; | |
52 | } | |
53 | ||
b2d3e335 HC |
54 | static void mask_loongarch_irq(struct irq_data *d) |
55 | { | |
56 | clear_csr_ecfg(ECFGF(d->hwirq)); | |
57 | } | |
58 | ||
59 | static void unmask_loongarch_irq(struct irq_data *d) | |
60 | { | |
61 | set_csr_ecfg(ECFGF(d->hwirq)); | |
62 | } | |
63 | ||
64 | static struct irq_chip cpu_irq_controller = { | |
65 | .name = "CPUINTC", | |
66 | .irq_mask = mask_loongarch_irq, | |
67 | .irq_unmask = unmask_loongarch_irq, | |
68 | }; | |
69 | ||
70 | static void handle_cpu_irq(struct pt_regs *regs) | |
71 | { | |
72 | int hwirq; | |
73 | unsigned int estat = read_csr_estat() & CSR_ESTAT_IS; | |
74 | ||
75 | while ((hwirq = ffs(estat))) { | |
76 | estat &= ~BIT(hwirq - 1); | |
77 | generic_handle_domain_irq(irq_domain, hwirq - 1); | |
78 | } | |
79 | } | |
80 | ||
81 | static int loongarch_cpu_intc_map(struct irq_domain *d, unsigned int irq, | |
82 | irq_hw_number_t hwirq) | |
83 | { | |
84 | irq_set_noprobe(irq); | |
85 | irq_set_chip_and_handler(irq, &cpu_irq_controller, handle_percpu_irq); | |
86 | ||
87 | return 0; | |
88 | } | |
89 | ||
90 | static const struct irq_domain_ops loongarch_cpu_intc_irq_domain_ops = { | |
91 | .map = loongarch_cpu_intc_map, | |
92 | .xlate = irq_domain_xlate_onecell, | |
93 | }; | |
94 | ||
95 | static int __init | |
96 | liointc_parse_madt(union acpi_subtable_headers *header, | |
97 | const unsigned long end) | |
98 | { | |
99 | struct acpi_madt_lio_pic *liointc_entry = (struct acpi_madt_lio_pic *)header; | |
100 | ||
101 | return liointc_acpi_init(irq_domain, liointc_entry); | |
102 | } | |
103 | ||
104 | static int __init | |
105 | eiointc_parse_madt(union acpi_subtable_headers *header, | |
106 | const unsigned long end) | |
107 | { | |
108 | struct acpi_madt_eio_pic *eiointc_entry = (struct acpi_madt_eio_pic *)header; | |
109 | ||
110 | return eiointc_acpi_init(irq_domain, eiointc_entry); | |
111 | } | |
112 | ||
113 | static int __init acpi_cascade_irqdomain_init(void) | |
114 | { | |
115 | acpi_table_parse_madt(ACPI_MADT_TYPE_LIO_PIC, | |
116 | liointc_parse_madt, 0); | |
117 | acpi_table_parse_madt(ACPI_MADT_TYPE_EIO_PIC, | |
118 | eiointc_parse_madt, 0); | |
119 | return 0; | |
120 | } | |
121 | ||
122 | static int __init cpuintc_acpi_init(union acpi_subtable_headers *header, | |
123 | const unsigned long end) | |
124 | { | |
125 | if (irq_domain) | |
126 | return 0; | |
127 | ||
128 | /* Mask interrupts. */ | |
129 | clear_csr_ecfg(ECFG0_IM); | |
130 | clear_csr_estat(ESTATF_IP); | |
131 | ||
7e4fd7a1 | 132 | cpuintc_handle = irq_domain_alloc_named_fwnode("CPUINTC"); |
b2d3e335 HC |
133 | irq_domain = irq_domain_create_linear(cpuintc_handle, EXCCODE_INT_NUM, |
134 | &loongarch_cpu_intc_irq_domain_ops, NULL); | |
135 | ||
136 | if (!irq_domain) | |
137 | panic("Failed to add irqdomain for LoongArch CPU"); | |
138 | ||
139 | set_handle_irq(&handle_cpu_irq); | |
e8bba72b JL |
140 | acpi_set_irq_model(ACPI_IRQ_MODEL_LPIC, lpic_get_gsi_domain_id); |
141 | acpi_set_gsi_to_irq_fallback(lpic_gsi_to_irq); | |
b2d3e335 HC |
142 | acpi_cascade_irqdomain_init(); |
143 | ||
144 | return 0; | |
145 | } | |
146 | ||
147 | IRQCHIP_ACPI_DECLARE(cpuintc_v1, ACPI_MADT_TYPE_CORE_PIC, | |
148 | NULL, ACPI_MADT_CORE_PIC_VERSION_V1, cpuintc_acpi_init); |