xref: /openbmc/linux/arch/loongarch/kernel/acpi.c (revision d4b6f1562a3c3284adcef81d6e4f183d7d34b8a9)
1628c3bb4SHuacai Chen // SPDX-License-Identifier: GPL-2.0
2628c3bb4SHuacai Chen /*
3628c3bb4SHuacai Chen  * acpi.c - Architecture-Specific Low-Level ACPI Boot Support
4628c3bb4SHuacai Chen  *
5628c3bb4SHuacai Chen  * Author: Jianmin Lv <lvjianmin@loongson.cn>
6628c3bb4SHuacai Chen  *         Huacai Chen <chenhuacai@loongson.cn>
7628c3bb4SHuacai Chen  * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
8628c3bb4SHuacai Chen  */
9628c3bb4SHuacai Chen 
10628c3bb4SHuacai Chen #include <linux/init.h>
11628c3bb4SHuacai Chen #include <linux/acpi.h>
12628c3bb4SHuacai Chen #include <linux/irq.h>
13628c3bb4SHuacai Chen #include <linux/irqdomain.h>
14628c3bb4SHuacai Chen #include <linux/memblock.h>
15628c3bb4SHuacai Chen #include <linux/serial_core.h>
16628c3bb4SHuacai Chen #include <asm/io.h>
17*d4b6f156SHuacai Chen #include <asm/numa.h>
18628c3bb4SHuacai Chen #include <asm/loongson.h>
19628c3bb4SHuacai Chen 
20628c3bb4SHuacai Chen int acpi_disabled;
21628c3bb4SHuacai Chen EXPORT_SYMBOL(acpi_disabled);
22628c3bb4SHuacai Chen int acpi_noirq;
23628c3bb4SHuacai Chen int acpi_pci_disabled;
24628c3bb4SHuacai Chen EXPORT_SYMBOL(acpi_pci_disabled);
25628c3bb4SHuacai Chen int acpi_strict = 1; /* We have no workarounds on LoongArch */
26628c3bb4SHuacai Chen int num_processors;
27628c3bb4SHuacai Chen int disabled_cpus;
28628c3bb4SHuacai Chen enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_PLATFORM;
29628c3bb4SHuacai Chen 
30628c3bb4SHuacai Chen u64 acpi_saved_sp;
31628c3bb4SHuacai Chen 
32628c3bb4SHuacai Chen #define MAX_CORE_PIC 256
33628c3bb4SHuacai Chen 
34628c3bb4SHuacai Chen #define PREFIX			"ACPI: "
35628c3bb4SHuacai Chen 
36628c3bb4SHuacai Chen int acpi_gsi_to_irq(u32 gsi, unsigned int *irqp)
37628c3bb4SHuacai Chen {
38628c3bb4SHuacai Chen 	if (irqp != NULL)
39628c3bb4SHuacai Chen 		*irqp = acpi_register_gsi(NULL, gsi, -1, -1);
40628c3bb4SHuacai Chen 	return (*irqp >= 0) ? 0 : -EINVAL;
41628c3bb4SHuacai Chen }
42628c3bb4SHuacai Chen EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
43628c3bb4SHuacai Chen 
44628c3bb4SHuacai Chen int acpi_isa_irq_to_gsi(unsigned int isa_irq, u32 *gsi)
45628c3bb4SHuacai Chen {
46628c3bb4SHuacai Chen 	if (gsi)
47628c3bb4SHuacai Chen 		*gsi = isa_irq;
48628c3bb4SHuacai Chen 	return 0;
49628c3bb4SHuacai Chen }
50628c3bb4SHuacai Chen 
51628c3bb4SHuacai Chen /*
52628c3bb4SHuacai Chen  * success: return IRQ number (>=0)
53628c3bb4SHuacai Chen  * failure: return < 0
54628c3bb4SHuacai Chen  */
55628c3bb4SHuacai Chen int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity)
56628c3bb4SHuacai Chen {
57628c3bb4SHuacai Chen 	struct irq_fwspec fwspec;
58628c3bb4SHuacai Chen 
59628c3bb4SHuacai Chen 	switch (gsi) {
60628c3bb4SHuacai Chen 	case GSI_MIN_CPU_IRQ ... GSI_MAX_CPU_IRQ:
61628c3bb4SHuacai Chen 		fwspec.fwnode = liointc_domain->fwnode;
62628c3bb4SHuacai Chen 		fwspec.param[0] = gsi - GSI_MIN_CPU_IRQ;
63628c3bb4SHuacai Chen 		fwspec.param_count = 1;
64628c3bb4SHuacai Chen 
65628c3bb4SHuacai Chen 		return irq_create_fwspec_mapping(&fwspec);
66628c3bb4SHuacai Chen 
67628c3bb4SHuacai Chen 	case GSI_MIN_LPC_IRQ ... GSI_MAX_LPC_IRQ:
68628c3bb4SHuacai Chen 		if (!pch_lpc_domain)
69628c3bb4SHuacai Chen 			return -EINVAL;
70628c3bb4SHuacai Chen 
71628c3bb4SHuacai Chen 		fwspec.fwnode = pch_lpc_domain->fwnode;
72628c3bb4SHuacai Chen 		fwspec.param[0] = gsi - GSI_MIN_LPC_IRQ;
73628c3bb4SHuacai Chen 		fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
74628c3bb4SHuacai Chen 		fwspec.param_count = 2;
75628c3bb4SHuacai Chen 
76628c3bb4SHuacai Chen 		return irq_create_fwspec_mapping(&fwspec);
77628c3bb4SHuacai Chen 
78628c3bb4SHuacai Chen 	case GSI_MIN_PCH_IRQ ... GSI_MAX_PCH_IRQ:
79628c3bb4SHuacai Chen 		if (!pch_pic_domain[0])
80628c3bb4SHuacai Chen 			return -EINVAL;
81628c3bb4SHuacai Chen 
82628c3bb4SHuacai Chen 		fwspec.fwnode = pch_pic_domain[0]->fwnode;
83628c3bb4SHuacai Chen 		fwspec.param[0] = gsi - GSI_MIN_PCH_IRQ;
84628c3bb4SHuacai Chen 		fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH;
85628c3bb4SHuacai Chen 		fwspec.param_count = 2;
86628c3bb4SHuacai Chen 
87628c3bb4SHuacai Chen 		return irq_create_fwspec_mapping(&fwspec);
88628c3bb4SHuacai Chen 	}
89628c3bb4SHuacai Chen 
90628c3bb4SHuacai Chen 	return -EINVAL;
91628c3bb4SHuacai Chen }
92628c3bb4SHuacai Chen EXPORT_SYMBOL_GPL(acpi_register_gsi);
93628c3bb4SHuacai Chen 
94628c3bb4SHuacai Chen void acpi_unregister_gsi(u32 gsi)
95628c3bb4SHuacai Chen {
96628c3bb4SHuacai Chen 
97628c3bb4SHuacai Chen }
98628c3bb4SHuacai Chen EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
99628c3bb4SHuacai Chen 
100628c3bb4SHuacai Chen void __init __iomem * __acpi_map_table(unsigned long phys, unsigned long size)
101628c3bb4SHuacai Chen {
102628c3bb4SHuacai Chen 
103628c3bb4SHuacai Chen 	if (!phys || !size)
104628c3bb4SHuacai Chen 		return NULL;
105628c3bb4SHuacai Chen 
106628c3bb4SHuacai Chen 	return early_memremap(phys, size);
107628c3bb4SHuacai Chen }
108628c3bb4SHuacai Chen void __init __acpi_unmap_table(void __iomem *map, unsigned long size)
109628c3bb4SHuacai Chen {
110628c3bb4SHuacai Chen 	if (!map || !size)
111628c3bb4SHuacai Chen 		return;
112628c3bb4SHuacai Chen 
113628c3bb4SHuacai Chen 	early_memunmap(map, size);
114628c3bb4SHuacai Chen }
115628c3bb4SHuacai Chen 
116628c3bb4SHuacai Chen void __init __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size)
117628c3bb4SHuacai Chen {
118628c3bb4SHuacai Chen 	if (!memblock_is_memory(phys))
119628c3bb4SHuacai Chen 		return ioremap(phys, size);
120628c3bb4SHuacai Chen 	else
121628c3bb4SHuacai Chen 		return ioremap_cache(phys, size);
122628c3bb4SHuacai Chen }
123628c3bb4SHuacai Chen 
124628c3bb4SHuacai Chen void __init acpi_boot_table_init(void)
125628c3bb4SHuacai Chen {
126628c3bb4SHuacai Chen 	/*
127628c3bb4SHuacai Chen 	 * If acpi_disabled, bail out
128628c3bb4SHuacai Chen 	 */
129628c3bb4SHuacai Chen 	if (acpi_disabled)
130628c3bb4SHuacai Chen 		return;
131628c3bb4SHuacai Chen 
132628c3bb4SHuacai Chen 	/*
133628c3bb4SHuacai Chen 	 * Initialize the ACPI boot-time table parser.
134628c3bb4SHuacai Chen 	 */
135628c3bb4SHuacai Chen 	if (acpi_table_init()) {
136628c3bb4SHuacai Chen 		disable_acpi();
137628c3bb4SHuacai Chen 		return;
138628c3bb4SHuacai Chen 	}
139628c3bb4SHuacai Chen }
140628c3bb4SHuacai Chen 
14146859ac8SHuacai Chen static int set_processor_mask(u32 id, u32 flags)
14246859ac8SHuacai Chen {
14346859ac8SHuacai Chen 
14446859ac8SHuacai Chen 	int cpu, cpuid = id;
14546859ac8SHuacai Chen 
14646859ac8SHuacai Chen 	if (num_processors >= nr_cpu_ids) {
14746859ac8SHuacai Chen 		pr_warn(PREFIX "nr_cpus/possible_cpus limit of %i reached."
14846859ac8SHuacai Chen 			" processor 0x%x ignored.\n", nr_cpu_ids, cpuid);
14946859ac8SHuacai Chen 
15046859ac8SHuacai Chen 		return -ENODEV;
15146859ac8SHuacai Chen 
15246859ac8SHuacai Chen 	}
15346859ac8SHuacai Chen 	if (cpuid == loongson_sysconf.boot_cpu_id)
15446859ac8SHuacai Chen 		cpu = 0;
15546859ac8SHuacai Chen 	else
15646859ac8SHuacai Chen 		cpu = cpumask_next_zero(-1, cpu_present_mask);
15746859ac8SHuacai Chen 
15846859ac8SHuacai Chen 	if (flags & ACPI_MADT_ENABLED) {
15946859ac8SHuacai Chen 		num_processors++;
16046859ac8SHuacai Chen 		set_cpu_possible(cpu, true);
16146859ac8SHuacai Chen 		set_cpu_present(cpu, true);
16246859ac8SHuacai Chen 		__cpu_number_map[cpuid] = cpu;
16346859ac8SHuacai Chen 		__cpu_logical_map[cpu] = cpuid;
16446859ac8SHuacai Chen 	} else
16546859ac8SHuacai Chen 		disabled_cpus++;
16646859ac8SHuacai Chen 
16746859ac8SHuacai Chen 	return cpu;
16846859ac8SHuacai Chen }
16946859ac8SHuacai Chen 
170628c3bb4SHuacai Chen static void __init acpi_process_madt(void)
171628c3bb4SHuacai Chen {
17246859ac8SHuacai Chen 	int i;
17346859ac8SHuacai Chen 
17446859ac8SHuacai Chen 	for (i = 0; i < NR_CPUS; i++) {
17546859ac8SHuacai Chen 		__cpu_number_map[i] = -1;
17646859ac8SHuacai Chen 		__cpu_logical_map[i] = -1;
17746859ac8SHuacai Chen 	}
17846859ac8SHuacai Chen 
179628c3bb4SHuacai Chen 	loongson_sysconf.nr_cpus = num_processors;
180628c3bb4SHuacai Chen }
181628c3bb4SHuacai Chen 
182628c3bb4SHuacai Chen int __init acpi_boot_init(void)
183628c3bb4SHuacai Chen {
184628c3bb4SHuacai Chen 	/*
185628c3bb4SHuacai Chen 	 * If acpi_disabled, bail out
186628c3bb4SHuacai Chen 	 */
187628c3bb4SHuacai Chen 	if (acpi_disabled)
188628c3bb4SHuacai Chen 		return -1;
189628c3bb4SHuacai Chen 
190628c3bb4SHuacai Chen 	loongson_sysconf.boot_cpu_id = read_csr_cpuid();
191628c3bb4SHuacai Chen 
192628c3bb4SHuacai Chen 	/*
193628c3bb4SHuacai Chen 	 * Process the Multiple APIC Description Table (MADT), if present
194628c3bb4SHuacai Chen 	 */
195628c3bb4SHuacai Chen 	acpi_process_madt();
196628c3bb4SHuacai Chen 
197628c3bb4SHuacai Chen 	/* Do not enable ACPI SPCR console by default */
198628c3bb4SHuacai Chen 	acpi_parse_spcr(earlycon_acpi_spcr_enable, false);
199628c3bb4SHuacai Chen 
200628c3bb4SHuacai Chen 	return 0;
201628c3bb4SHuacai Chen }
202628c3bb4SHuacai Chen 
203*d4b6f156SHuacai Chen #ifdef CONFIG_ACPI_NUMA
204*d4b6f156SHuacai Chen 
205*d4b6f156SHuacai Chen static __init int setup_node(int pxm)
206*d4b6f156SHuacai Chen {
207*d4b6f156SHuacai Chen 	return acpi_map_pxm_to_node(pxm);
208*d4b6f156SHuacai Chen }
209*d4b6f156SHuacai Chen 
210*d4b6f156SHuacai Chen /*
211*d4b6f156SHuacai Chen  * Callback for SLIT parsing.  pxm_to_node() returns NUMA_NO_NODE for
212*d4b6f156SHuacai Chen  * I/O localities since SRAT does not list them.  I/O localities are
213*d4b6f156SHuacai Chen  * not supported at this point.
214*d4b6f156SHuacai Chen  */
215*d4b6f156SHuacai Chen unsigned int numa_distance_cnt;
216*d4b6f156SHuacai Chen 
217*d4b6f156SHuacai Chen static inline unsigned int get_numa_distances_cnt(struct acpi_table_slit *slit)
218*d4b6f156SHuacai Chen {
219*d4b6f156SHuacai Chen 	return slit->locality_count;
220*d4b6f156SHuacai Chen }
221*d4b6f156SHuacai Chen 
222*d4b6f156SHuacai Chen void __init numa_set_distance(int from, int to, int distance)
223*d4b6f156SHuacai Chen {
224*d4b6f156SHuacai Chen 	if ((u8)distance != distance || (from == to && distance != LOCAL_DISTANCE)) {
225*d4b6f156SHuacai Chen 		pr_warn_once("Warning: invalid distance parameter, from=%d to=%d distance=%d\n",
226*d4b6f156SHuacai Chen 				from, to, distance);
227*d4b6f156SHuacai Chen 		return;
228*d4b6f156SHuacai Chen 	}
229*d4b6f156SHuacai Chen 
230*d4b6f156SHuacai Chen 	node_distances[from][to] = distance;
231*d4b6f156SHuacai Chen }
232*d4b6f156SHuacai Chen 
233*d4b6f156SHuacai Chen /* Callback for Proximity Domain -> CPUID mapping */
234*d4b6f156SHuacai Chen void __init
235*d4b6f156SHuacai Chen acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa)
236*d4b6f156SHuacai Chen {
237*d4b6f156SHuacai Chen 	int pxm, node;
238*d4b6f156SHuacai Chen 
239*d4b6f156SHuacai Chen 	if (srat_disabled())
240*d4b6f156SHuacai Chen 		return;
241*d4b6f156SHuacai Chen 	if (pa->header.length != sizeof(struct acpi_srat_cpu_affinity)) {
242*d4b6f156SHuacai Chen 		bad_srat();
243*d4b6f156SHuacai Chen 		return;
244*d4b6f156SHuacai Chen 	}
245*d4b6f156SHuacai Chen 	if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0)
246*d4b6f156SHuacai Chen 		return;
247*d4b6f156SHuacai Chen 	pxm = pa->proximity_domain_lo;
248*d4b6f156SHuacai Chen 	if (acpi_srat_revision >= 2) {
249*d4b6f156SHuacai Chen 		pxm |= (pa->proximity_domain_hi[0] << 8);
250*d4b6f156SHuacai Chen 		pxm |= (pa->proximity_domain_hi[1] << 16);
251*d4b6f156SHuacai Chen 		pxm |= (pa->proximity_domain_hi[2] << 24);
252*d4b6f156SHuacai Chen 	}
253*d4b6f156SHuacai Chen 	node = setup_node(pxm);
254*d4b6f156SHuacai Chen 	if (node < 0) {
255*d4b6f156SHuacai Chen 		pr_err("SRAT: Too many proximity domains %x\n", pxm);
256*d4b6f156SHuacai Chen 		bad_srat();
257*d4b6f156SHuacai Chen 		return;
258*d4b6f156SHuacai Chen 	}
259*d4b6f156SHuacai Chen 
260*d4b6f156SHuacai Chen 	if (pa->apic_id >= CONFIG_NR_CPUS) {
261*d4b6f156SHuacai Chen 		pr_info("SRAT: PXM %u -> CPU 0x%02x -> Node %u skipped apicid that is too big\n",
262*d4b6f156SHuacai Chen 				pxm, pa->apic_id, node);
263*d4b6f156SHuacai Chen 		return;
264*d4b6f156SHuacai Chen 	}
265*d4b6f156SHuacai Chen 
266*d4b6f156SHuacai Chen 	early_numa_add_cpu(pa->apic_id, node);
267*d4b6f156SHuacai Chen 
268*d4b6f156SHuacai Chen 	set_cpuid_to_node(pa->apic_id, node);
269*d4b6f156SHuacai Chen 	node_set(node, numa_nodes_parsed);
270*d4b6f156SHuacai Chen 	pr_info("SRAT: PXM %u -> CPU 0x%02x -> Node %u\n", pxm, pa->apic_id, node);
271*d4b6f156SHuacai Chen }
272*d4b6f156SHuacai Chen 
273*d4b6f156SHuacai Chen void __init acpi_numa_arch_fixup(void) {}
274*d4b6f156SHuacai Chen #endif
275*d4b6f156SHuacai Chen 
276628c3bb4SHuacai Chen void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size)
277628c3bb4SHuacai Chen {
278628c3bb4SHuacai Chen 	memblock_reserve(addr, size);
279628c3bb4SHuacai Chen }
28046859ac8SHuacai Chen 
28146859ac8SHuacai Chen #ifdef CONFIG_ACPI_HOTPLUG_CPU
28246859ac8SHuacai Chen 
28346859ac8SHuacai Chen #include <acpi/processor.h>
28446859ac8SHuacai Chen 
285*d4b6f156SHuacai Chen static int __ref acpi_map_cpu2node(acpi_handle handle, int cpu, int physid)
286*d4b6f156SHuacai Chen {
287*d4b6f156SHuacai Chen #ifdef CONFIG_ACPI_NUMA
288*d4b6f156SHuacai Chen 	int nid;
289*d4b6f156SHuacai Chen 
290*d4b6f156SHuacai Chen 	nid = acpi_get_node(handle);
291*d4b6f156SHuacai Chen 	if (nid != NUMA_NO_NODE) {
292*d4b6f156SHuacai Chen 		set_cpuid_to_node(physid, nid);
293*d4b6f156SHuacai Chen 		node_set(nid, numa_nodes_parsed);
294*d4b6f156SHuacai Chen 		set_cpu_numa_node(cpu, nid);
295*d4b6f156SHuacai Chen 		cpumask_set_cpu(cpu, cpumask_of_node(nid));
296*d4b6f156SHuacai Chen 	}
297*d4b6f156SHuacai Chen #endif
298*d4b6f156SHuacai Chen 	return 0;
299*d4b6f156SHuacai Chen }
300*d4b6f156SHuacai Chen 
30146859ac8SHuacai Chen int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 acpi_id, int *pcpu)
30246859ac8SHuacai Chen {
30346859ac8SHuacai Chen 	int cpu;
30446859ac8SHuacai Chen 
30546859ac8SHuacai Chen 	cpu = set_processor_mask(physid, ACPI_MADT_ENABLED);
30646859ac8SHuacai Chen 	if (cpu < 0) {
30746859ac8SHuacai Chen 		pr_info(PREFIX "Unable to map lapic to logical cpu number\n");
30846859ac8SHuacai Chen 		return cpu;
30946859ac8SHuacai Chen 	}
31046859ac8SHuacai Chen 
311*d4b6f156SHuacai Chen 	acpi_map_cpu2node(handle, cpu, physid);
312*d4b6f156SHuacai Chen 
31346859ac8SHuacai Chen 	*pcpu = cpu;
31446859ac8SHuacai Chen 
31546859ac8SHuacai Chen 	return 0;
31646859ac8SHuacai Chen }
31746859ac8SHuacai Chen EXPORT_SYMBOL(acpi_map_cpu);
31846859ac8SHuacai Chen 
31946859ac8SHuacai Chen int acpi_unmap_cpu(int cpu)
32046859ac8SHuacai Chen {
321*d4b6f156SHuacai Chen #ifdef CONFIG_ACPI_NUMA
322*d4b6f156SHuacai Chen 	set_cpuid_to_node(cpu_logical_map(cpu), NUMA_NO_NODE);
323*d4b6f156SHuacai Chen #endif
32446859ac8SHuacai Chen 	set_cpu_present(cpu, false);
32546859ac8SHuacai Chen 	num_processors--;
32646859ac8SHuacai Chen 
32746859ac8SHuacai Chen 	pr_info("cpu%d hot remove!\n", cpu);
32846859ac8SHuacai Chen 
32946859ac8SHuacai Chen 	return 0;
33046859ac8SHuacai Chen }
33146859ac8SHuacai Chen EXPORT_SYMBOL(acpi_unmap_cpu);
33246859ac8SHuacai Chen 
33346859ac8SHuacai Chen #endif /* CONFIG_ACPI_HOTPLUG_CPU */
334