]>
Commit | Line | Data |
---|---|---|
e790f1de WD |
1 | /* |
2 | * This program is free software; you can redistribute it and/or modify | |
3 | * it under the terms of the GNU General Public License version 2 as | |
4 | * published by the Free Software Foundation. | |
5 | * | |
6 | * This program is distributed in the hope that it will be useful, | |
7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
9 | * GNU General Public License for more details. | |
10 | * | |
11 | * Copyright (C) 2013 ARM Limited | |
12 | * | |
13 | * Author: Will Deacon <will.deacon@arm.com> | |
14 | */ | |
15 | ||
16 | #define pr_fmt(fmt) "psci: " fmt | |
17 | ||
18 | #include <linux/init.h> | |
19 | #include <linux/of.h> | |
00ef54bb | 20 | #include <linux/smp.h> |
c814ca02 | 21 | #include <linux/delay.h> |
bff60792 | 22 | #include <linux/psci.h> |
18910ab0 | 23 | #include <linux/slab.h> |
bff60792 | 24 | |
e71246a2 | 25 | #include <uapi/linux/psci.h> |
e790f1de WD |
26 | |
27 | #include <asm/compiler.h> | |
cd1aebf5 | 28 | #include <asm/cpu_ops.h> |
e790f1de | 29 | #include <asm/errno.h> |
00ef54bb | 30 | #include <asm/smp_plat.h> |
18910ab0 | 31 | #include <asm/suspend.h> |
00ef54bb | 32 | |
c8cc4273 | 33 | static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state); |
18910ab0 | 34 | |
819a8826 | 35 | static int __maybe_unused cpu_psci_cpu_init_idle(unsigned int cpu) |
18910ab0 LP |
36 | { |
37 | int i, ret, count = 0; | |
c8cc4273 | 38 | u32 *psci_states; |
819a8826 LP |
39 | struct device_node *state_node, *cpu_node; |
40 | ||
41 | cpu_node = of_get_cpu_node(cpu, NULL); | |
42 | if (!cpu_node) | |
43 | return -ENODEV; | |
18910ab0 LP |
44 | |
45 | /* | |
46 | * If the PSCI cpu_suspend function hook has not been initialized | |
47 | * idle states must not be enabled, so bail out | |
48 | */ | |
49 | if (!psci_ops.cpu_suspend) | |
50 | return -EOPNOTSUPP; | |
51 | ||
52 | /* Count idle states */ | |
53 | while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states", | |
54 | count))) { | |
55 | count++; | |
56 | of_node_put(state_node); | |
57 | } | |
58 | ||
59 | if (!count) | |
60 | return -ENODEV; | |
61 | ||
62 | psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL); | |
63 | if (!psci_states) | |
64 | return -ENOMEM; | |
65 | ||
66 | for (i = 0; i < count; i++) { | |
c8cc4273 | 67 | u32 state; |
18910ab0 LP |
68 | |
69 | state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); | |
70 | ||
71 | ret = of_property_read_u32(state_node, | |
72 | "arm,psci-suspend-param", | |
c8cc4273 | 73 | &state); |
18910ab0 LP |
74 | if (ret) { |
75 | pr_warn(" * %s missing arm,psci-suspend-param property\n", | |
76 | state_node->full_name); | |
77 | of_node_put(state_node); | |
78 | goto free_mem; | |
79 | } | |
80 | ||
81 | of_node_put(state_node); | |
c8cc4273 MR |
82 | pr_debug("psci-power-state %#x index %d\n", state, i); |
83 | if (!psci_power_state_is_valid(state)) { | |
84 | pr_warn("Invalid PSCI power state %#x\n", state); | |
85 | ret = -EINVAL; | |
86 | goto free_mem; | |
87 | } | |
88 | psci_states[i] = state; | |
18910ab0 LP |
89 | } |
90 | /* Idle states parsed correctly, initialize per-cpu pointer */ | |
91 | per_cpu(psci_power_state, cpu) = psci_states; | |
92 | return 0; | |
93 | ||
94 | free_mem: | |
95 | kfree(psci_states); | |
96 | return ret; | |
97 | } | |
98 | ||
819a8826 | 99 | static int __init cpu_psci_cpu_init(unsigned int cpu) |
00ef54bb MR |
100 | { |
101 | return 0; | |
102 | } | |
103 | ||
cd1aebf5 | 104 | static int __init cpu_psci_cpu_prepare(unsigned int cpu) |
00ef54bb | 105 | { |
00ef54bb MR |
106 | if (!psci_ops.cpu_on) { |
107 | pr_err("no cpu_on method, not booting CPU%d\n", cpu); | |
108 | return -ENODEV; | |
109 | } | |
110 | ||
00ef54bb MR |
111 | return 0; |
112 | } | |
113 | ||
652af899 MR |
114 | static int cpu_psci_cpu_boot(unsigned int cpu) |
115 | { | |
116 | int err = psci_ops.cpu_on(cpu_logical_map(cpu), __pa(secondary_entry)); | |
117 | if (err) | |
288ac26c | 118 | pr_err("failed to boot CPU%d (%d)\n", cpu, err); |
652af899 MR |
119 | |
120 | return err; | |
121 | } | |
122 | ||
831ccf79 MR |
123 | #ifdef CONFIG_HOTPLUG_CPU |
124 | static int cpu_psci_cpu_disable(unsigned int cpu) | |
125 | { | |
126 | /* Fail early if we don't have CPU_OFF support */ | |
127 | if (!psci_ops.cpu_off) | |
128 | return -EOPNOTSUPP; | |
ff3010e6 MR |
129 | |
130 | /* Trusted OS will deny CPU_OFF */ | |
131 | if (psci_tos_resident_on(cpu)) | |
132 | return -EPERM; | |
133 | ||
831ccf79 MR |
134 | return 0; |
135 | } | |
136 | ||
137 | static void cpu_psci_cpu_die(unsigned int cpu) | |
138 | { | |
139 | int ret; | |
140 | /* | |
141 | * There are no known implementations of PSCI actually using the | |
142 | * power state field, pass a sensible default for now. | |
143 | */ | |
c8cc4273 MR |
144 | u32 state = PSCI_POWER_STATE_TYPE_POWER_DOWN << |
145 | PSCI_0_2_POWER_STATE_TYPE_SHIFT; | |
831ccf79 MR |
146 | |
147 | ret = psci_ops.cpu_off(state); | |
148 | ||
288ac26c | 149 | pr_crit("unable to power off CPU%u (%d)\n", cpu, ret); |
831ccf79 | 150 | } |
c814ca02 AC |
151 | |
152 | static int cpu_psci_cpu_kill(unsigned int cpu) | |
153 | { | |
154 | int err, i; | |
155 | ||
156 | if (!psci_ops.affinity_info) | |
6b99c68c | 157 | return 0; |
c814ca02 AC |
158 | /* |
159 | * cpu_kill could race with cpu_die and we can | |
160 | * potentially end up declaring this cpu undead | |
161 | * while it is dying. So, try again a few times. | |
162 | */ | |
163 | ||
164 | for (i = 0; i < 10; i++) { | |
165 | err = psci_ops.affinity_info(cpu_logical_map(cpu), 0); | |
166 | if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) { | |
167 | pr_info("CPU%d killed.\n", cpu); | |
6b99c68c | 168 | return 0; |
c814ca02 AC |
169 | } |
170 | ||
171 | msleep(10); | |
172 | pr_info("Retrying again to check for CPU kill\n"); | |
173 | } | |
174 | ||
175 | pr_warn("CPU%d may not have shut down cleanly (AFFINITY_INFO reports %d)\n", | |
176 | cpu, err); | |
6b99c68c | 177 | return -ETIMEDOUT; |
c814ca02 | 178 | } |
831ccf79 MR |
179 | #endif |
180 | ||
18910ab0 LP |
181 | static int psci_suspend_finisher(unsigned long index) |
182 | { | |
c8cc4273 | 183 | u32 *state = __this_cpu_read(psci_power_state); |
18910ab0 LP |
184 | |
185 | return psci_ops.cpu_suspend(state[index - 1], | |
186 | virt_to_phys(cpu_resume)); | |
187 | } | |
188 | ||
189 | static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index) | |
190 | { | |
191 | int ret; | |
c8cc4273 | 192 | u32 *state = __this_cpu_read(psci_power_state); |
18910ab0 LP |
193 | /* |
194 | * idle state index 0 corresponds to wfi, should never be called | |
195 | * from the cpu_suspend operations | |
196 | */ | |
197 | if (WARN_ON_ONCE(!index)) | |
198 | return -EINVAL; | |
199 | ||
c8cc4273 | 200 | if (!psci_power_state_loses_context(state[index - 1])) |
18910ab0 LP |
201 | ret = psci_ops.cpu_suspend(state[index - 1], 0); |
202 | else | |
af391b15 | 203 | ret = cpu_suspend(index, psci_suspend_finisher); |
18910ab0 LP |
204 | |
205 | return ret; | |
206 | } | |
207 | ||
cd1aebf5 | 208 | const struct cpu_operations cpu_psci_ops = { |
00ef54bb | 209 | .name = "psci", |
18910ab0 LP |
210 | #ifdef CONFIG_CPU_IDLE |
211 | .cpu_init_idle = cpu_psci_cpu_init_idle, | |
212 | .cpu_suspend = cpu_psci_cpu_suspend, | |
213 | #endif | |
cd1aebf5 MR |
214 | .cpu_init = cpu_psci_cpu_init, |
215 | .cpu_prepare = cpu_psci_cpu_prepare, | |
652af899 | 216 | .cpu_boot = cpu_psci_cpu_boot, |
831ccf79 MR |
217 | #ifdef CONFIG_HOTPLUG_CPU |
218 | .cpu_disable = cpu_psci_cpu_disable, | |
219 | .cpu_die = cpu_psci_cpu_die, | |
c814ca02 | 220 | .cpu_kill = cpu_psci_cpu_kill, |
831ccf79 | 221 | #endif |
00ef54bb MR |
222 | }; |
223 |