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/serial_core.h> 16 #include <asm/io.h> 17 #include <asm/numa.h> 18 #include <asm/loongson.h> 19 20 int acpi_disabled; 21 EXPORT_SYMBOL(acpi_disabled); 22 int acpi_noirq; 23 int acpi_pci_disabled; 24 EXPORT_SYMBOL(acpi_pci_disabled); 25 int acpi_strict = 1; /* We have no workarounds on LoongArch */ 26 int num_processors; 27 int disabled_cpus; 28 enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_PLATFORM; 29 30 u64 acpi_saved_sp; 31 32 #define MAX_CORE_PIC 256 33 34 #define PREFIX "ACPI: " 35 36 int acpi_gsi_to_irq(u32 gsi, unsigned int *irqp) 37 { 38 if (irqp != NULL) 39 *irqp = acpi_register_gsi(NULL, gsi, -1, -1); 40 return (*irqp >= 0) ? 0 : -EINVAL; 41 } 42 EXPORT_SYMBOL_GPL(acpi_gsi_to_irq); 43 44 int acpi_isa_irq_to_gsi(unsigned int isa_irq, u32 *gsi) 45 { 46 if (gsi) 47 *gsi = isa_irq; 48 return 0; 49 } 50 51 /* 52 * success: return IRQ number (>=0) 53 * failure: return < 0 54 */ 55 int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity) 56 { 57 struct irq_fwspec fwspec; 58 59 switch (gsi) { 60 case GSI_MIN_CPU_IRQ ... GSI_MAX_CPU_IRQ: 61 fwspec.fwnode = liointc_domain->fwnode; 62 fwspec.param[0] = gsi - GSI_MIN_CPU_IRQ; 63 fwspec.param_count = 1; 64 65 return irq_create_fwspec_mapping(&fwspec); 66 67 case GSI_MIN_LPC_IRQ ... GSI_MAX_LPC_IRQ: 68 if (!pch_lpc_domain) 69 return -EINVAL; 70 71 fwspec.fwnode = pch_lpc_domain->fwnode; 72 fwspec.param[0] = gsi - GSI_MIN_LPC_IRQ; 73 fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity); 74 fwspec.param_count = 2; 75 76 return irq_create_fwspec_mapping(&fwspec); 77 78 case GSI_MIN_PCH_IRQ ... GSI_MAX_PCH_IRQ: 79 if (!pch_pic_domain[0]) 80 return -EINVAL; 81 82 fwspec.fwnode = pch_pic_domain[0]->fwnode; 83 fwspec.param[0] = gsi - GSI_MIN_PCH_IRQ; 84 fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH; 85 fwspec.param_count = 2; 86 87 return irq_create_fwspec_mapping(&fwspec); 88 } 89 90 return -EINVAL; 91 } 92 EXPORT_SYMBOL_GPL(acpi_register_gsi); 93 94 void acpi_unregister_gsi(u32 gsi) 95 { 96 97 } 98 EXPORT_SYMBOL_GPL(acpi_unregister_gsi); 99 100 void __init __iomem * __acpi_map_table(unsigned long phys, unsigned long size) 101 { 102 103 if (!phys || !size) 104 return NULL; 105 106 return early_memremap(phys, size); 107 } 108 void __init __acpi_unmap_table(void __iomem *map, unsigned long size) 109 { 110 if (!map || !size) 111 return; 112 113 early_memunmap(map, size); 114 } 115 116 void __init __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size) 117 { 118 if (!memblock_is_memory(phys)) 119 return ioremap(phys, size); 120 else 121 return ioremap_cache(phys, size); 122 } 123 124 void __init acpi_boot_table_init(void) 125 { 126 /* 127 * If acpi_disabled, bail out 128 */ 129 if (acpi_disabled) 130 return; 131 132 /* 133 * Initialize the ACPI boot-time table parser. 134 */ 135 if (acpi_table_init()) { 136 disable_acpi(); 137 return; 138 } 139 } 140 141 static int set_processor_mask(u32 id, u32 flags) 142 { 143 144 int cpu, cpuid = id; 145 146 if (num_processors >= nr_cpu_ids) { 147 pr_warn(PREFIX "nr_cpus/possible_cpus limit of %i reached." 148 " processor 0x%x ignored.\n", nr_cpu_ids, cpuid); 149 150 return -ENODEV; 151 152 } 153 if (cpuid == loongson_sysconf.boot_cpu_id) 154 cpu = 0; 155 else 156 cpu = cpumask_next_zero(-1, cpu_present_mask); 157 158 if (flags & ACPI_MADT_ENABLED) { 159 num_processors++; 160 set_cpu_possible(cpu, true); 161 set_cpu_present(cpu, true); 162 __cpu_number_map[cpuid] = cpu; 163 __cpu_logical_map[cpu] = cpuid; 164 } else 165 disabled_cpus++; 166 167 return cpu; 168 } 169 170 static void __init acpi_process_madt(void) 171 { 172 int i; 173 174 for (i = 0; i < NR_CPUS; i++) { 175 __cpu_number_map[i] = -1; 176 __cpu_logical_map[i] = -1; 177 } 178 179 loongson_sysconf.nr_cpus = num_processors; 180 } 181 182 int __init acpi_boot_init(void) 183 { 184 /* 185 * If acpi_disabled, bail out 186 */ 187 if (acpi_disabled) 188 return -1; 189 190 loongson_sysconf.boot_cpu_id = read_csr_cpuid(); 191 192 /* 193 * Process the Multiple APIC Description Table (MADT), if present 194 */ 195 acpi_process_madt(); 196 197 /* Do not enable ACPI SPCR console by default */ 198 acpi_parse_spcr(earlycon_acpi_spcr_enable, false); 199 200 return 0; 201 } 202 203 #ifdef CONFIG_ACPI_NUMA 204 205 static __init int setup_node(int pxm) 206 { 207 return acpi_map_pxm_to_node(pxm); 208 } 209 210 /* 211 * Callback for SLIT parsing. pxm_to_node() returns NUMA_NO_NODE for 212 * I/O localities since SRAT does not list them. I/O localities are 213 * not supported at this point. 214 */ 215 unsigned int numa_distance_cnt; 216 217 static inline unsigned int get_numa_distances_cnt(struct acpi_table_slit *slit) 218 { 219 return slit->locality_count; 220 } 221 222 void __init numa_set_distance(int from, int to, int distance) 223 { 224 if ((u8)distance != distance || (from == to && distance != LOCAL_DISTANCE)) { 225 pr_warn_once("Warning: invalid distance parameter, from=%d to=%d distance=%d\n", 226 from, to, distance); 227 return; 228 } 229 230 node_distances[from][to] = distance; 231 } 232 233 /* Callback for Proximity Domain -> CPUID mapping */ 234 void __init 235 acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) 236 { 237 int pxm, node; 238 239 if (srat_disabled()) 240 return; 241 if (pa->header.length != sizeof(struct acpi_srat_cpu_affinity)) { 242 bad_srat(); 243 return; 244 } 245 if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0) 246 return; 247 pxm = pa->proximity_domain_lo; 248 if (acpi_srat_revision >= 2) { 249 pxm |= (pa->proximity_domain_hi[0] << 8); 250 pxm |= (pa->proximity_domain_hi[1] << 16); 251 pxm |= (pa->proximity_domain_hi[2] << 24); 252 } 253 node = setup_node(pxm); 254 if (node < 0) { 255 pr_err("SRAT: Too many proximity domains %x\n", pxm); 256 bad_srat(); 257 return; 258 } 259 260 if (pa->apic_id >= CONFIG_NR_CPUS) { 261 pr_info("SRAT: PXM %u -> CPU 0x%02x -> Node %u skipped apicid that is too big\n", 262 pxm, pa->apic_id, node); 263 return; 264 } 265 266 early_numa_add_cpu(pa->apic_id, node); 267 268 set_cpuid_to_node(pa->apic_id, node); 269 node_set(node, numa_nodes_parsed); 270 pr_info("SRAT: PXM %u -> CPU 0x%02x -> Node %u\n", pxm, pa->apic_id, node); 271 } 272 273 void __init acpi_numa_arch_fixup(void) {} 274 #endif 275 276 void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size) 277 { 278 memblock_reserve(addr, size); 279 } 280 281 #ifdef CONFIG_ACPI_HOTPLUG_CPU 282 283 #include <acpi/processor.h> 284 285 static int __ref acpi_map_cpu2node(acpi_handle handle, int cpu, int physid) 286 { 287 #ifdef CONFIG_ACPI_NUMA 288 int nid; 289 290 nid = acpi_get_node(handle); 291 if (nid != NUMA_NO_NODE) { 292 set_cpuid_to_node(physid, nid); 293 node_set(nid, numa_nodes_parsed); 294 set_cpu_numa_node(cpu, nid); 295 cpumask_set_cpu(cpu, cpumask_of_node(nid)); 296 } 297 #endif 298 return 0; 299 } 300 301 int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 acpi_id, int *pcpu) 302 { 303 int cpu; 304 305 cpu = set_processor_mask(physid, ACPI_MADT_ENABLED); 306 if (cpu < 0) { 307 pr_info(PREFIX "Unable to map lapic to logical cpu number\n"); 308 return cpu; 309 } 310 311 acpi_map_cpu2node(handle, cpu, physid); 312 313 *pcpu = cpu; 314 315 return 0; 316 } 317 EXPORT_SYMBOL(acpi_map_cpu); 318 319 int acpi_unmap_cpu(int cpu) 320 { 321 #ifdef CONFIG_ACPI_NUMA 322 set_cpuid_to_node(cpu_logical_map(cpu), NUMA_NO_NODE); 323 #endif 324 set_cpu_present(cpu, false); 325 num_processors--; 326 327 pr_info("cpu%d hot remove!\n", cpu); 328 329 return 0; 330 } 331 EXPORT_SYMBOL(acpi_unmap_cpu); 332 333 #endif /* CONFIG_ACPI_HOTPLUG_CPU */ 334