1 /* 2 * CPU kernel entry/exit control 3 * 4 * Copyright (C) 2013 ARM Ltd. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 #include <linux/acpi.h> 20 #include <linux/cache.h> 21 #include <linux/errno.h> 22 #include <linux/of.h> 23 #include <linux/string.h> 24 #include <asm/acpi.h> 25 #include <asm/cpu_ops.h> 26 #include <asm/smp_plat.h> 27 28 extern const struct cpu_operations smp_spin_table_ops; 29 extern const struct cpu_operations acpi_parking_protocol_ops; 30 extern const struct cpu_operations cpu_psci_ops; 31 32 const struct cpu_operations *cpu_ops[NR_CPUS] __ro_after_init; 33 34 static const struct cpu_operations *dt_supported_cpu_ops[] __initconst = { 35 &smp_spin_table_ops, 36 &cpu_psci_ops, 37 NULL, 38 }; 39 40 static const struct cpu_operations *acpi_supported_cpu_ops[] __initconst = { 41 #ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL 42 &acpi_parking_protocol_ops, 43 #endif 44 &cpu_psci_ops, 45 NULL, 46 }; 47 48 static const struct cpu_operations * __init cpu_get_ops(const char *name) 49 { 50 const struct cpu_operations **ops; 51 52 ops = acpi_disabled ? dt_supported_cpu_ops : acpi_supported_cpu_ops; 53 54 while (*ops) { 55 if (!strcmp(name, (*ops)->name)) 56 return *ops; 57 58 ops++; 59 } 60 61 return NULL; 62 } 63 64 static const char *__init cpu_read_enable_method(int cpu) 65 { 66 const char *enable_method; 67 68 if (acpi_disabled) { 69 struct device_node *dn = of_get_cpu_node(cpu, NULL); 70 71 if (!dn) { 72 if (!cpu) 73 pr_err("Failed to find device node for boot cpu\n"); 74 return NULL; 75 } 76 77 enable_method = of_get_property(dn, "enable-method", NULL); 78 if (!enable_method) { 79 /* 80 * The boot CPU may not have an enable method (e.g. 81 * when spin-table is used for secondaries). 82 * Don't warn spuriously. 83 */ 84 if (cpu != 0) 85 pr_err("%s: missing enable-method property\n", 86 dn->full_name); 87 } 88 } else { 89 enable_method = acpi_get_enable_method(cpu); 90 if (!enable_method) { 91 /* 92 * In ACPI systems the boot CPU does not require 93 * checking the enable method since for some 94 * boot protocol (ie parking protocol) it need not 95 * be initialized. Don't warn spuriously. 96 */ 97 if (cpu != 0) 98 pr_err("Unsupported ACPI enable-method\n"); 99 } 100 } 101 102 return enable_method; 103 } 104 /* 105 * Read a cpu's enable method and record it in cpu_ops. 106 */ 107 int __init cpu_read_ops(int cpu) 108 { 109 const char *enable_method = cpu_read_enable_method(cpu); 110 111 if (!enable_method) 112 return -ENODEV; 113 114 cpu_ops[cpu] = cpu_get_ops(enable_method); 115 if (!cpu_ops[cpu]) { 116 pr_warn("Unsupported enable-method: %s\n", enable_method); 117 return -EOPNOTSUPP; 118 } 119 120 return 0; 121 } 122