1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * acpi.c - Architecture-Specific Low-Level ACPI Boot Support |
4 | * |
5 | * Author: Jianmin Lv <lvjianmin@loongson.cn> |
6 | * Huacai Chen <chenhuacai@loongson.cn> |
7 | * Copyright (C) 2020-2022 Loongson Technology Corporation Limited |
8 | */ |
9 | |
10 | #include <linux/init.h> |
11 | #include <linux/acpi.h> |
12 | #include <linux/irq.h> |
13 | #include <linux/irqdomain.h> |
14 | #include <linux/memblock.h> |
15 | #include <linux/of_fdt.h> |
16 | #include <linux/serial_core.h> |
17 | #include <asm/io.h> |
18 | #include <asm/numa.h> |
19 | #include <asm/loongson.h> |
20 | |
21 | int acpi_disabled; |
22 | EXPORT_SYMBOL(acpi_disabled); |
23 | int acpi_noirq; |
24 | int acpi_pci_disabled; |
25 | EXPORT_SYMBOL(acpi_pci_disabled); |
26 | int acpi_strict = 1; /* We have no workarounds on LoongArch */ |
27 | int num_processors; |
28 | int disabled_cpus; |
29 | |
30 | u64 acpi_saved_sp; |
31 | |
32 | #define PREFIX "ACPI: " |
33 | |
34 | struct acpi_madt_core_pic acpi_core_pic[MAX_CORE_PIC]; |
35 | |
36 | void __init __iomem * __acpi_map_table(unsigned long phys, unsigned long size) |
37 | { |
38 | |
39 | if (!phys || !size) |
40 | return NULL; |
41 | |
42 | return early_memremap(phys_addr: phys, size); |
43 | } |
44 | void __init __acpi_unmap_table(void __iomem *map, unsigned long size) |
45 | { |
46 | if (!map || !size) |
47 | return; |
48 | |
49 | early_memunmap(addr: map, size); |
50 | } |
51 | |
52 | void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size) |
53 | { |
54 | if (!memblock_is_memory(addr: phys)) |
55 | return ioremap(offset: phys, size); |
56 | else |
57 | return ioremap_cache(offset: phys, size); |
58 | } |
59 | |
60 | #ifdef CONFIG_SMP |
61 | static int set_processor_mask(u32 id, u32 flags) |
62 | { |
63 | |
64 | int cpu, cpuid = id; |
65 | |
66 | if (num_processors >= nr_cpu_ids) { |
67 | pr_warn(PREFIX "nr_cpus/possible_cpus limit of %i reached." |
68 | " processor 0x%x ignored.\n" , nr_cpu_ids, cpuid); |
69 | |
70 | return -ENODEV; |
71 | |
72 | } |
73 | if (cpuid == loongson_sysconf.boot_cpu_id) |
74 | cpu = 0; |
75 | else |
76 | cpu = cpumask_next_zero(n: -1, cpu_present_mask); |
77 | |
78 | if (flags & ACPI_MADT_ENABLED) { |
79 | num_processors++; |
80 | set_cpu_possible(cpu, possible: true); |
81 | set_cpu_present(cpu, present: true); |
82 | __cpu_number_map[cpuid] = cpu; |
83 | __cpu_logical_map[cpu] = cpuid; |
84 | } else |
85 | disabled_cpus++; |
86 | |
87 | return cpu; |
88 | } |
89 | #endif |
90 | |
91 | static int __init |
92 | acpi_parse_processor(union acpi_subtable_headers *, const unsigned long end) |
93 | { |
94 | struct acpi_madt_core_pic *processor = NULL; |
95 | |
96 | processor = (struct acpi_madt_core_pic *)header; |
97 | if (BAD_MADT_ENTRY(processor, end)) |
98 | return -EINVAL; |
99 | |
100 | acpi_table_print_madt_entry(madt: &header->common); |
101 | #ifdef CONFIG_SMP |
102 | acpi_core_pic[processor->core_id] = *processor; |
103 | set_processor_mask(id: processor->core_id, flags: processor->flags); |
104 | #endif |
105 | |
106 | return 0; |
107 | } |
108 | |
109 | static int __init |
110 | acpi_parse_eio_master(union acpi_subtable_headers *, const unsigned long end) |
111 | { |
112 | static int core = 0; |
113 | struct acpi_madt_eio_pic *eiointc = NULL; |
114 | |
115 | eiointc = (struct acpi_madt_eio_pic *)header; |
116 | if (BAD_MADT_ENTRY(eiointc, end)) |
117 | return -EINVAL; |
118 | |
119 | core = eiointc->node * CORES_PER_EIO_NODE; |
120 | set_bit(nr: core, addr: loongson_sysconf.cores_io_master); |
121 | |
122 | return 0; |
123 | } |
124 | |
125 | static void __init acpi_process_madt(void) |
126 | { |
127 | #ifdef CONFIG_SMP |
128 | int i; |
129 | |
130 | for (i = 0; i < NR_CPUS; i++) { |
131 | __cpu_number_map[i] = -1; |
132 | __cpu_logical_map[i] = -1; |
133 | } |
134 | #endif |
135 | acpi_table_parse_madt(id: ACPI_MADT_TYPE_CORE_PIC, |
136 | handler: acpi_parse_processor, max_entries: MAX_CORE_PIC); |
137 | |
138 | acpi_table_parse_madt(id: ACPI_MADT_TYPE_EIO_PIC, |
139 | handler: acpi_parse_eio_master, max_entries: MAX_IO_PICS); |
140 | |
141 | loongson_sysconf.nr_cpus = num_processors; |
142 | } |
143 | |
144 | int pptt_enabled; |
145 | |
146 | int __init parse_acpi_topology(void) |
147 | { |
148 | int cpu, topology_id; |
149 | |
150 | for_each_possible_cpu(cpu) { |
151 | topology_id = find_acpi_cpu_topology(cpu, level: 0); |
152 | if (topology_id < 0) { |
153 | pr_warn("Invalid BIOS PPTT\n" ); |
154 | return -ENOENT; |
155 | } |
156 | |
157 | if (acpi_pptt_cpu_is_thread(cpu) <= 0) |
158 | cpu_data[cpu].core = topology_id; |
159 | else { |
160 | topology_id = find_acpi_cpu_topology(cpu, level: 1); |
161 | if (topology_id < 0) |
162 | return -ENOENT; |
163 | |
164 | cpu_data[cpu].core = topology_id; |
165 | } |
166 | } |
167 | |
168 | pptt_enabled = 1; |
169 | |
170 | return 0; |
171 | } |
172 | |
173 | #ifndef CONFIG_SUSPEND |
174 | int (*acpi_suspend_lowlevel)(void); |
175 | #else |
176 | int (*acpi_suspend_lowlevel)(void) = loongarch_acpi_suspend; |
177 | #endif |
178 | |
179 | void __init acpi_boot_table_init(void) |
180 | { |
181 | /* |
182 | * If acpi_disabled, bail out |
183 | */ |
184 | if (acpi_disabled) |
185 | goto fdt_earlycon; |
186 | |
187 | /* |
188 | * Initialize the ACPI boot-time table parser. |
189 | */ |
190 | if (acpi_table_init()) { |
191 | disable_acpi(); |
192 | goto fdt_earlycon; |
193 | } |
194 | |
195 | loongson_sysconf.boot_cpu_id = read_csr_cpuid(); |
196 | |
197 | /* |
198 | * Process the Multiple APIC Description Table (MADT), if present |
199 | */ |
200 | acpi_process_madt(); |
201 | |
202 | /* Do not enable ACPI SPCR console by default */ |
203 | acpi_parse_spcr(enable_earlycon: earlycon_acpi_spcr_enable, enable_console: false); |
204 | |
205 | return; |
206 | |
207 | fdt_earlycon: |
208 | if (earlycon_acpi_spcr_enable) |
209 | early_init_dt_scan_chosen_stdout(); |
210 | } |
211 | |
212 | #ifdef CONFIG_ACPI_NUMA |
213 | |
214 | static __init int setup_node(int pxm) |
215 | { |
216 | return acpi_map_pxm_to_node(pxm); |
217 | } |
218 | |
219 | /* |
220 | * Callback for SLIT parsing. pxm_to_node() returns NUMA_NO_NODE for |
221 | * I/O localities since SRAT does not list them. I/O localities are |
222 | * not supported at this point. |
223 | */ |
224 | unsigned int numa_distance_cnt; |
225 | |
226 | static inline unsigned int get_numa_distances_cnt(struct acpi_table_slit *slit) |
227 | { |
228 | return slit->locality_count; |
229 | } |
230 | |
231 | void __init numa_set_distance(int from, int to, int distance) |
232 | { |
233 | if ((u8)distance != distance || (from == to && distance != LOCAL_DISTANCE)) { |
234 | pr_warn_once("Warning: invalid distance parameter, from=%d to=%d distance=%d\n" , |
235 | from, to, distance); |
236 | return; |
237 | } |
238 | |
239 | node_distances[from][to] = distance; |
240 | } |
241 | |
242 | /* Callback for Proximity Domain -> CPUID mapping */ |
243 | void __init |
244 | acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) |
245 | { |
246 | int pxm, node; |
247 | |
248 | if (srat_disabled()) |
249 | return; |
250 | if (pa->header.length != sizeof(struct acpi_srat_cpu_affinity)) { |
251 | bad_srat(); |
252 | return; |
253 | } |
254 | if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0) |
255 | return; |
256 | pxm = pa->proximity_domain_lo; |
257 | if (acpi_srat_revision >= 2) { |
258 | pxm |= (pa->proximity_domain_hi[0] << 8); |
259 | pxm |= (pa->proximity_domain_hi[1] << 16); |
260 | pxm |= (pa->proximity_domain_hi[2] << 24); |
261 | } |
262 | node = setup_node(pxm); |
263 | if (node < 0) { |
264 | pr_err("SRAT: Too many proximity domains %x\n" , pxm); |
265 | bad_srat(); |
266 | return; |
267 | } |
268 | |
269 | if (pa->apic_id >= CONFIG_NR_CPUS) { |
270 | pr_info("SRAT: PXM %u -> CPU 0x%02x -> Node %u skipped apicid that is too big\n" , |
271 | pxm, pa->apic_id, node); |
272 | return; |
273 | } |
274 | |
275 | early_numa_add_cpu(pa->apic_id, node); |
276 | |
277 | set_cpuid_to_node(pa->apic_id, node); |
278 | node_set(node, numa_nodes_parsed); |
279 | pr_info("SRAT: PXM %u -> CPU 0x%02x -> Node %u\n" , pxm, pa->apic_id, node); |
280 | } |
281 | |
282 | #endif |
283 | |
284 | void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size) |
285 | { |
286 | memblock_reserve(base: addr, size); |
287 | } |
288 | |
289 | #ifdef CONFIG_ACPI_HOTPLUG_CPU |
290 | |
291 | #include <acpi/processor.h> |
292 | |
293 | static int __ref acpi_map_cpu2node(acpi_handle handle, int cpu, int physid) |
294 | { |
295 | #ifdef CONFIG_ACPI_NUMA |
296 | int nid; |
297 | |
298 | nid = acpi_get_node(handle); |
299 | if (nid != NUMA_NO_NODE) { |
300 | set_cpuid_to_node(physid, nid); |
301 | node_set(nid, numa_nodes_parsed); |
302 | set_cpu_numa_node(cpu, node: nid); |
303 | cpumask_set_cpu(cpu, dstp: cpumask_of_node(node: nid)); |
304 | } |
305 | #endif |
306 | return 0; |
307 | } |
308 | |
309 | int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 acpi_id, int *pcpu) |
310 | { |
311 | int cpu; |
312 | |
313 | cpu = set_processor_mask(id: physid, ACPI_MADT_ENABLED); |
314 | if (cpu < 0) { |
315 | pr_info(PREFIX "Unable to map lapic to logical cpu number\n" ); |
316 | return cpu; |
317 | } |
318 | |
319 | acpi_map_cpu2node(handle, cpu, physid); |
320 | |
321 | *pcpu = cpu; |
322 | |
323 | return 0; |
324 | } |
325 | EXPORT_SYMBOL(acpi_map_cpu); |
326 | |
327 | int acpi_unmap_cpu(int cpu) |
328 | { |
329 | #ifdef CONFIG_ACPI_NUMA |
330 | set_cpuid_to_node(cpu_logical_map(cpu), NUMA_NO_NODE); |
331 | #endif |
332 | set_cpu_present(cpu, present: false); |
333 | num_processors--; |
334 | |
335 | pr_info("cpu%d hot remove!\n" , cpu); |
336 | |
337 | return 0; |
338 | } |
339 | EXPORT_SYMBOL(acpi_unmap_cpu); |
340 | |
341 | #endif /* CONFIG_ACPI_HOTPLUG_CPU */ |
342 | |