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