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/errno.h> 21 #include <linux/of.h> 22 #include <linux/string.h> 23 #include <asm/acpi.h> 24 #include <asm/cpu_ops.h> 25 #include <asm/smp_plat.h> 26 27 extern const struct cpu_operations smp_spin_table_ops; 28 extern const struct cpu_operations acpi_parking_protocol_ops; 29 extern const struct cpu_operations cpu_psci_ops; 30 31 const struct cpu_operations *cpu_ops[NR_CPUS]; 32 33 static const struct cpu_operations *dt_supported_cpu_ops[] __initconst = { 34 &smp_spin_table_ops, 35 &cpu_psci_ops, 36 NULL, 37 }; 38 39 static const struct cpu_operations *acpi_supported_cpu_ops[] __initconst = { 40 #ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL 41 &acpi_parking_protocol_ops, 42 #endif 43 &cpu_psci_ops, 44 NULL, 45 }; 46 47 static const struct cpu_operations * __init cpu_get_ops(const char *name) 48 { 49 const struct cpu_operations **ops; 50 51 ops = acpi_disabled ? dt_supported_cpu_ops : acpi_supported_cpu_ops; 52 53 while (*ops) { 54 if (!strcmp(name, (*ops)->name)) 55 return *ops; 56 57 ops++; 58 } 59 60 return NULL; 61 } 62 63 static const char *__init cpu_read_enable_method(int cpu) 64 { 65 const char *enable_method; 66 67 if (acpi_disabled) { 68 struct device_node *dn = of_get_cpu_node(cpu, NULL); 69 70 if (!dn) { 71 if (!cpu) 72 pr_err("Failed to find device node for boot cpu\n"); 73 return NULL; 74 } 75 76 enable_method = of_get_property(dn, "enable-method", NULL); 77 if (!enable_method) { 78 /* 79 * The boot CPU may not have an enable method (e.g. 80 * when spin-table is used for secondaries). 81 * Don't warn spuriously. 82 */ 83 if (cpu != 0) 84 pr_err("%s: missing enable-method property\n", 85 dn->full_name); 86 } 87 } else { 88 enable_method = acpi_get_enable_method(cpu); 89 if (!enable_method) { 90 /* 91 * In ACPI systems the boot CPU does not require 92 * checking the enable method since for some 93 * boot protocol (ie parking protocol) it need not 94 * be initialized. Don't warn spuriously. 95 */ 96 if (cpu != 0) 97 pr_err("Unsupported ACPI enable-method\n"); 98 } 99 } 100 101 return enable_method; 102 } 103 /* 104 * Read a cpu's enable method and record it in cpu_ops. 105 */ 106 int __init cpu_read_ops(int cpu) 107 { 108 const char *enable_method = cpu_read_enable_method(cpu); 109 110 if (!enable_method) 111 return -ENODEV; 112 113 cpu_ops[cpu] = cpu_get_ops(enable_method); 114 if (!cpu_ops[cpu]) { 115 pr_warn("Unsupported enable-method: %s\n", enable_method); 116 return -EOPNOTSUPP; 117 } 118 119 return 0; 120 } 121