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> 17d4b6f156SHuacai 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 29628c3bb4SHuacai Chen u64 acpi_saved_sp; 30628c3bb4SHuacai Chen 31628c3bb4SHuacai Chen #define MAX_CORE_PIC 256 32628c3bb4SHuacai Chen 33628c3bb4SHuacai Chen #define PREFIX "ACPI: " 34628c3bb4SHuacai Chen 35628c3bb4SHuacai Chen void __init __iomem * __acpi_map_table(unsigned long phys, unsigned long size) 36628c3bb4SHuacai Chen { 37628c3bb4SHuacai Chen 38628c3bb4SHuacai Chen if (!phys || !size) 39628c3bb4SHuacai Chen return NULL; 40628c3bb4SHuacai Chen 41628c3bb4SHuacai Chen return early_memremap(phys, size); 42628c3bb4SHuacai Chen } 43628c3bb4SHuacai Chen void __init __acpi_unmap_table(void __iomem *map, unsigned long size) 44628c3bb4SHuacai Chen { 45628c3bb4SHuacai Chen if (!map || !size) 46628c3bb4SHuacai Chen return; 47628c3bb4SHuacai Chen 48628c3bb4SHuacai Chen early_memunmap(map, size); 49628c3bb4SHuacai Chen } 50628c3bb4SHuacai Chen 51e0fba87cSHuacai Chen void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size) 52628c3bb4SHuacai Chen { 53628c3bb4SHuacai Chen if (!memblock_is_memory(phys)) 54628c3bb4SHuacai Chen return ioremap(phys, size); 55628c3bb4SHuacai Chen else 56628c3bb4SHuacai Chen return ioremap_cache(phys, size); 57628c3bb4SHuacai Chen } 58628c3bb4SHuacai Chen 59255b4658SHuacai Chen #ifdef CONFIG_SMP 6046859ac8SHuacai Chen static int set_processor_mask(u32 id, u32 flags) 6146859ac8SHuacai Chen { 6246859ac8SHuacai Chen 6346859ac8SHuacai Chen int cpu, cpuid = id; 6446859ac8SHuacai Chen 6546859ac8SHuacai Chen if (num_processors >= nr_cpu_ids) { 6646859ac8SHuacai Chen pr_warn(PREFIX "nr_cpus/possible_cpus limit of %i reached." 6746859ac8SHuacai Chen " processor 0x%x ignored.\n", nr_cpu_ids, cpuid); 6846859ac8SHuacai Chen 6946859ac8SHuacai Chen return -ENODEV; 7046859ac8SHuacai Chen 7146859ac8SHuacai Chen } 7246859ac8SHuacai Chen if (cpuid == loongson_sysconf.boot_cpu_id) 7346859ac8SHuacai Chen cpu = 0; 7446859ac8SHuacai Chen else 7546859ac8SHuacai Chen cpu = cpumask_next_zero(-1, cpu_present_mask); 7646859ac8SHuacai Chen 7746859ac8SHuacai Chen if (flags & ACPI_MADT_ENABLED) { 7846859ac8SHuacai Chen num_processors++; 7946859ac8SHuacai Chen set_cpu_possible(cpu, true); 8046859ac8SHuacai Chen set_cpu_present(cpu, true); 8146859ac8SHuacai Chen __cpu_number_map[cpuid] = cpu; 8246859ac8SHuacai Chen __cpu_logical_map[cpu] = cpuid; 8346859ac8SHuacai Chen } else 8446859ac8SHuacai Chen disabled_cpus++; 8546859ac8SHuacai Chen 8646859ac8SHuacai Chen return cpu; 8746859ac8SHuacai Chen } 88255b4658SHuacai Chen #endif 8946859ac8SHuacai Chen 90e9e7ff16SHuacai Chen static int __init 91e9e7ff16SHuacai Chen acpi_parse_processor(union acpi_subtable_headers *header, const unsigned long end) 92e9e7ff16SHuacai Chen { 93e9e7ff16SHuacai Chen struct acpi_madt_core_pic *processor = NULL; 94e9e7ff16SHuacai Chen 95e9e7ff16SHuacai Chen processor = (struct acpi_madt_core_pic *)header; 96e9e7ff16SHuacai Chen if (BAD_MADT_ENTRY(processor, end)) 97e9e7ff16SHuacai Chen return -EINVAL; 98e9e7ff16SHuacai Chen 99e9e7ff16SHuacai Chen acpi_table_print_madt_entry(&header->common); 100e9e7ff16SHuacai Chen #ifdef CONFIG_SMP 101e9e7ff16SHuacai Chen set_processor_mask(processor->core_id, processor->flags); 102e9e7ff16SHuacai Chen #endif 103e9e7ff16SHuacai Chen 104e9e7ff16SHuacai Chen return 0; 105e9e7ff16SHuacai Chen } 106e9e7ff16SHuacai Chen 107e9e7ff16SHuacai Chen static int __init 108e9e7ff16SHuacai Chen acpi_parse_eio_master(union acpi_subtable_headers *header, const unsigned long end) 109e9e7ff16SHuacai Chen { 110e9e7ff16SHuacai Chen static int core = 0; 111e9e7ff16SHuacai Chen struct acpi_madt_eio_pic *eiointc = NULL; 112e9e7ff16SHuacai Chen 113e9e7ff16SHuacai Chen eiointc = (struct acpi_madt_eio_pic *)header; 114e9e7ff16SHuacai Chen if (BAD_MADT_ENTRY(eiointc, end)) 115e9e7ff16SHuacai Chen return -EINVAL; 116e9e7ff16SHuacai Chen 117e9e7ff16SHuacai Chen core = eiointc->node * CORES_PER_EIO_NODE; 118e9e7ff16SHuacai Chen set_bit(core, &(loongson_sysconf.cores_io_master)); 119e9e7ff16SHuacai Chen 120e9e7ff16SHuacai Chen return 0; 121e9e7ff16SHuacai Chen } 122e9e7ff16SHuacai Chen 123628c3bb4SHuacai Chen static void __init acpi_process_madt(void) 124628c3bb4SHuacai Chen { 125255b4658SHuacai Chen #ifdef CONFIG_SMP 12646859ac8SHuacai Chen int i; 12746859ac8SHuacai Chen 12846859ac8SHuacai Chen for (i = 0; i < NR_CPUS; i++) { 12946859ac8SHuacai Chen __cpu_number_map[i] = -1; 13046859ac8SHuacai Chen __cpu_logical_map[i] = -1; 13146859ac8SHuacai Chen } 132255b4658SHuacai Chen #endif 133e9e7ff16SHuacai Chen acpi_table_parse_madt(ACPI_MADT_TYPE_CORE_PIC, 134e9e7ff16SHuacai Chen acpi_parse_processor, MAX_CORE_PIC); 135e9e7ff16SHuacai Chen 136e9e7ff16SHuacai Chen acpi_table_parse_madt(ACPI_MADT_TYPE_EIO_PIC, 137e9e7ff16SHuacai Chen acpi_parse_eio_master, MAX_IO_PICS); 13846859ac8SHuacai Chen 139628c3bb4SHuacai Chen loongson_sysconf.nr_cpus = num_processors; 140628c3bb4SHuacai Chen } 141628c3bb4SHuacai Chen 142*538eafc6SHuacai Chen void __init acpi_boot_table_init(void) 143628c3bb4SHuacai Chen { 144628c3bb4SHuacai Chen /* 145628c3bb4SHuacai Chen * If acpi_disabled, bail out 146628c3bb4SHuacai Chen */ 147628c3bb4SHuacai Chen if (acpi_disabled) 148*538eafc6SHuacai Chen return; 149*538eafc6SHuacai Chen 150*538eafc6SHuacai Chen /* 151*538eafc6SHuacai Chen * Initialize the ACPI boot-time table parser. 152*538eafc6SHuacai Chen */ 153*538eafc6SHuacai Chen if (acpi_table_init()) { 154*538eafc6SHuacai Chen disable_acpi(); 155*538eafc6SHuacai Chen return; 156*538eafc6SHuacai Chen } 157628c3bb4SHuacai Chen 158628c3bb4SHuacai Chen loongson_sysconf.boot_cpu_id = read_csr_cpuid(); 159628c3bb4SHuacai Chen 160628c3bb4SHuacai Chen /* 161628c3bb4SHuacai Chen * Process the Multiple APIC Description Table (MADT), if present 162628c3bb4SHuacai Chen */ 163628c3bb4SHuacai Chen acpi_process_madt(); 164628c3bb4SHuacai Chen 165628c3bb4SHuacai Chen /* Do not enable ACPI SPCR console by default */ 166628c3bb4SHuacai Chen acpi_parse_spcr(earlycon_acpi_spcr_enable, false); 167628c3bb4SHuacai Chen } 168628c3bb4SHuacai Chen 169d4b6f156SHuacai Chen #ifdef CONFIG_ACPI_NUMA 170d4b6f156SHuacai Chen 171d4b6f156SHuacai Chen static __init int setup_node(int pxm) 172d4b6f156SHuacai Chen { 173d4b6f156SHuacai Chen return acpi_map_pxm_to_node(pxm); 174d4b6f156SHuacai Chen } 175d4b6f156SHuacai Chen 176d4b6f156SHuacai Chen /* 177d4b6f156SHuacai Chen * Callback for SLIT parsing. pxm_to_node() returns NUMA_NO_NODE for 178d4b6f156SHuacai Chen * I/O localities since SRAT does not list them. I/O localities are 179d4b6f156SHuacai Chen * not supported at this point. 180d4b6f156SHuacai Chen */ 181d4b6f156SHuacai Chen unsigned int numa_distance_cnt; 182d4b6f156SHuacai Chen 183d4b6f156SHuacai Chen static inline unsigned int get_numa_distances_cnt(struct acpi_table_slit *slit) 184d4b6f156SHuacai Chen { 185d4b6f156SHuacai Chen return slit->locality_count; 186d4b6f156SHuacai Chen } 187d4b6f156SHuacai Chen 188d4b6f156SHuacai Chen void __init numa_set_distance(int from, int to, int distance) 189d4b6f156SHuacai Chen { 190d4b6f156SHuacai Chen if ((u8)distance != distance || (from == to && distance != LOCAL_DISTANCE)) { 191d4b6f156SHuacai Chen pr_warn_once("Warning: invalid distance parameter, from=%d to=%d distance=%d\n", 192d4b6f156SHuacai Chen from, to, distance); 193d4b6f156SHuacai Chen return; 194d4b6f156SHuacai Chen } 195d4b6f156SHuacai Chen 196d4b6f156SHuacai Chen node_distances[from][to] = distance; 197d4b6f156SHuacai Chen } 198d4b6f156SHuacai Chen 199d4b6f156SHuacai Chen /* Callback for Proximity Domain -> CPUID mapping */ 200d4b6f156SHuacai Chen void __init 201d4b6f156SHuacai Chen acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) 202d4b6f156SHuacai Chen { 203d4b6f156SHuacai Chen int pxm, node; 204d4b6f156SHuacai Chen 205d4b6f156SHuacai Chen if (srat_disabled()) 206d4b6f156SHuacai Chen return; 207d4b6f156SHuacai Chen if (pa->header.length != sizeof(struct acpi_srat_cpu_affinity)) { 208d4b6f156SHuacai Chen bad_srat(); 209d4b6f156SHuacai Chen return; 210d4b6f156SHuacai Chen } 211d4b6f156SHuacai Chen if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0) 212d4b6f156SHuacai Chen return; 213d4b6f156SHuacai Chen pxm = pa->proximity_domain_lo; 214d4b6f156SHuacai Chen if (acpi_srat_revision >= 2) { 215d4b6f156SHuacai Chen pxm |= (pa->proximity_domain_hi[0] << 8); 216d4b6f156SHuacai Chen pxm |= (pa->proximity_domain_hi[1] << 16); 217d4b6f156SHuacai Chen pxm |= (pa->proximity_domain_hi[2] << 24); 218d4b6f156SHuacai Chen } 219d4b6f156SHuacai Chen node = setup_node(pxm); 220d4b6f156SHuacai Chen if (node < 0) { 221d4b6f156SHuacai Chen pr_err("SRAT: Too many proximity domains %x\n", pxm); 222d4b6f156SHuacai Chen bad_srat(); 223d4b6f156SHuacai Chen return; 224d4b6f156SHuacai Chen } 225d4b6f156SHuacai Chen 226d4b6f156SHuacai Chen if (pa->apic_id >= CONFIG_NR_CPUS) { 227d4b6f156SHuacai Chen pr_info("SRAT: PXM %u -> CPU 0x%02x -> Node %u skipped apicid that is too big\n", 228d4b6f156SHuacai Chen pxm, pa->apic_id, node); 229d4b6f156SHuacai Chen return; 230d4b6f156SHuacai Chen } 231d4b6f156SHuacai Chen 232d4b6f156SHuacai Chen early_numa_add_cpu(pa->apic_id, node); 233d4b6f156SHuacai Chen 234d4b6f156SHuacai Chen set_cpuid_to_node(pa->apic_id, node); 235d4b6f156SHuacai Chen node_set(node, numa_nodes_parsed); 236d4b6f156SHuacai Chen pr_info("SRAT: PXM %u -> CPU 0x%02x -> Node %u\n", pxm, pa->apic_id, node); 237d4b6f156SHuacai Chen } 238d4b6f156SHuacai Chen 239d4b6f156SHuacai Chen void __init acpi_numa_arch_fixup(void) {} 240d4b6f156SHuacai Chen #endif 241d4b6f156SHuacai Chen 242628c3bb4SHuacai Chen void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size) 243628c3bb4SHuacai Chen { 244628c3bb4SHuacai Chen memblock_reserve(addr, size); 245628c3bb4SHuacai Chen } 24646859ac8SHuacai Chen 24746859ac8SHuacai Chen #ifdef CONFIG_ACPI_HOTPLUG_CPU 24846859ac8SHuacai Chen 24946859ac8SHuacai Chen #include <acpi/processor.h> 25046859ac8SHuacai Chen 251d4b6f156SHuacai Chen static int __ref acpi_map_cpu2node(acpi_handle handle, int cpu, int physid) 252d4b6f156SHuacai Chen { 253d4b6f156SHuacai Chen #ifdef CONFIG_ACPI_NUMA 254d4b6f156SHuacai Chen int nid; 255d4b6f156SHuacai Chen 256d4b6f156SHuacai Chen nid = acpi_get_node(handle); 257d4b6f156SHuacai Chen if (nid != NUMA_NO_NODE) { 258d4b6f156SHuacai Chen set_cpuid_to_node(physid, nid); 259d4b6f156SHuacai Chen node_set(nid, numa_nodes_parsed); 260d4b6f156SHuacai Chen set_cpu_numa_node(cpu, nid); 261d4b6f156SHuacai Chen cpumask_set_cpu(cpu, cpumask_of_node(nid)); 262d4b6f156SHuacai Chen } 263d4b6f156SHuacai Chen #endif 264d4b6f156SHuacai Chen return 0; 265d4b6f156SHuacai Chen } 266d4b6f156SHuacai Chen 26746859ac8SHuacai Chen int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 acpi_id, int *pcpu) 26846859ac8SHuacai Chen { 26946859ac8SHuacai Chen int cpu; 27046859ac8SHuacai Chen 27146859ac8SHuacai Chen cpu = set_processor_mask(physid, ACPI_MADT_ENABLED); 27246859ac8SHuacai Chen if (cpu < 0) { 27346859ac8SHuacai Chen pr_info(PREFIX "Unable to map lapic to logical cpu number\n"); 27446859ac8SHuacai Chen return cpu; 27546859ac8SHuacai Chen } 27646859ac8SHuacai Chen 277d4b6f156SHuacai Chen acpi_map_cpu2node(handle, cpu, physid); 278d4b6f156SHuacai Chen 27946859ac8SHuacai Chen *pcpu = cpu; 28046859ac8SHuacai Chen 28146859ac8SHuacai Chen return 0; 28246859ac8SHuacai Chen } 28346859ac8SHuacai Chen EXPORT_SYMBOL(acpi_map_cpu); 28446859ac8SHuacai Chen 28546859ac8SHuacai Chen int acpi_unmap_cpu(int cpu) 28646859ac8SHuacai Chen { 287d4b6f156SHuacai Chen #ifdef CONFIG_ACPI_NUMA 288d4b6f156SHuacai Chen set_cpuid_to_node(cpu_logical_map(cpu), NUMA_NO_NODE); 289d4b6f156SHuacai Chen #endif 29046859ac8SHuacai Chen set_cpu_present(cpu, false); 29146859ac8SHuacai Chen num_processors--; 29246859ac8SHuacai Chen 29346859ac8SHuacai Chen pr_info("cpu%d hot remove!\n", cpu); 29446859ac8SHuacai Chen 29546859ac8SHuacai Chen return 0; 29646859ac8SHuacai Chen } 29746859ac8SHuacai Chen EXPORT_SYMBOL(acpi_unmap_cpu); 29846859ac8SHuacai Chen 29946859ac8SHuacai Chen #endif /* CONFIG_ACPI_HOTPLUG_CPU */ 300