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