diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-10-06 19:18:23 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-10-06 19:18:23 -0400 |
commit | b2eed302b67014a3825331c63880bf228ac842eb (patch) | |
tree | 60abbecba4a09cab8c61fe1a4b8d3858fba5abec | |
parent | 49a09c9ab012017c4673b86dbb28c616cf8f2381 (diff) | |
parent | eeb1aa5f78caf8d5ec1ef651c09bde34370321d8 (diff) |
Merge branch 'pm-cpuidle'
* pm-cpuidle:
drivers: cpuidle: initialize big.LITTLE driver through DT
drivers: cpuidle: CPU idle ARM64 driver
drivers: cpuidle: implement DT based idle states infrastructure
cpuidle: big.LITTLE: add Exynos5800 compatible string
cpuidle: Replace strnicmp with strncasecmp
arm64: add PSCI CPU_SUSPEND based cpu_suspend support
arm64: kernel: introduce cpu_init_idle CPU operation
arm64: kernel: refactor the CPU suspend API for retention states
Documentation: arm: define DT idle states bindings
-rw-r--r-- | Documentation/devicetree/bindings/arm/cpus.txt | 8 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/arm/idle-states.txt | 679 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/arm/psci.txt | 14 | ||||
-rw-r--r-- | arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts | 23 | ||||
-rw-r--r-- | arch/arm64/include/asm/cpu_ops.h | 3 | ||||
-rw-r--r-- | arch/arm64/include/asm/cpuidle.h | 13 | ||||
-rw-r--r-- | arch/arm64/include/asm/suspend.h | 1 | ||||
-rw-r--r-- | arch/arm64/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/arm64/kernel/cpuidle.c | 31 | ||||
-rw-r--r-- | arch/arm64/kernel/psci.c | 104 | ||||
-rw-r--r-- | arch/arm64/kernel/sleep.S | 47 | ||||
-rw-r--r-- | arch/arm64/kernel/suspend.c | 48 | ||||
-rw-r--r-- | drivers/cpuidle/Kconfig | 8 | ||||
-rw-r--r-- | drivers/cpuidle/Kconfig.arm | 1 | ||||
-rw-r--r-- | drivers/cpuidle/Kconfig.arm64 | 14 | ||||
-rw-r--r-- | drivers/cpuidle/Makefile | 5 | ||||
-rw-r--r-- | drivers/cpuidle/cpuidle-arm64.c | 133 | ||||
-rw-r--r-- | drivers/cpuidle/cpuidle-big_little.c | 20 | ||||
-rw-r--r-- | drivers/cpuidle/dt_idle_states.c | 213 | ||||
-rw-r--r-- | drivers/cpuidle/dt_idle_states.h | 7 | ||||
-rw-r--r-- | drivers/cpuidle/governor.c | 2 |
21 files changed, 1341 insertions, 34 deletions
diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt index 298e2f6b33c6..6fd0f15e899a 100644 --- a/Documentation/devicetree/bindings/arm/cpus.txt +++ b/Documentation/devicetree/bindings/arm/cpus.txt | |||
@@ -219,6 +219,12 @@ nodes to be present and contain the properties described below. | |||
219 | Value type: <phandle> | 219 | Value type: <phandle> |
220 | Definition: Specifies the ACC[2] node associated with this CPU. | 220 | Definition: Specifies the ACC[2] node associated with this CPU. |
221 | 221 | ||
222 | - cpu-idle-states | ||
223 | Usage: Optional | ||
224 | Value type: <prop-encoded-array> | ||
225 | Definition: | ||
226 | # List of phandles to idle state nodes supported | ||
227 | by this cpu [3]. | ||
222 | 228 | ||
223 | Example 1 (dual-cluster big.LITTLE system 32-bit): | 229 | Example 1 (dual-cluster big.LITTLE system 32-bit): |
224 | 230 | ||
@@ -415,3 +421,5 @@ cpus { | |||
415 | -- | 421 | -- |
416 | [1] arm/msm/qcom,saw2.txt | 422 | [1] arm/msm/qcom,saw2.txt |
417 | [2] arm/msm/qcom,kpss-acc.txt | 423 | [2] arm/msm/qcom,kpss-acc.txt |
424 | [3] ARM Linux kernel documentation - idle states bindings | ||
425 | Documentation/devicetree/bindings/arm/idle-states.txt | ||
diff --git a/Documentation/devicetree/bindings/arm/idle-states.txt b/Documentation/devicetree/bindings/arm/idle-states.txt new file mode 100644 index 000000000000..37375c7f3ccc --- /dev/null +++ b/Documentation/devicetree/bindings/arm/idle-states.txt | |||
@@ -0,0 +1,679 @@ | |||
1 | ========================================== | ||
2 | ARM idle states binding description | ||
3 | ========================================== | ||
4 | |||
5 | ========================================== | ||
6 | 1 - Introduction | ||
7 | ========================================== | ||
8 | |||
9 | ARM systems contain HW capable of managing power consumption dynamically, | ||
10 | where cores can be put in different low-power states (ranging from simple | ||
11 | wfi to power gating) according to OS PM policies. The CPU states representing | ||
12 | the range of dynamic idle states that a processor can enter at run-time, can be | ||
13 | specified through device tree bindings representing the parameters required | ||
14 | to enter/exit specific idle states on a given processor. | ||
15 | |||
16 | According to the Server Base System Architecture document (SBSA, [3]), the | ||
17 | power states an ARM CPU can be put into are identified by the following list: | ||
18 | |||
19 | - Running | ||
20 | - Idle_standby | ||
21 | - Idle_retention | ||
22 | - Sleep | ||
23 | - Off | ||
24 | |||
25 | The power states described in the SBSA document define the basic CPU states on | ||
26 | top of which ARM platforms implement power management schemes that allow an OS | ||
27 | PM implementation to put the processor in different idle states (which include | ||
28 | states listed above; "off" state is not an idle state since it does not have | ||
29 | wake-up capabilities, hence it is not considered in this document). | ||
30 | |||
31 | Idle state parameters (eg entry latency) are platform specific and need to be | ||
32 | characterized with bindings that provide the required information to OS PM | ||
33 | code so that it can build the required tables and use them at runtime. | ||
34 | |||
35 | The device tree binding definition for ARM idle states is the subject of this | ||
36 | document. | ||
37 | |||
38 | =========================================== | ||
39 | 2 - idle-states definitions | ||
40 | =========================================== | ||
41 | |||
42 | Idle states are characterized for a specific system through a set of | ||
43 | timing and energy related properties, that underline the HW behaviour | ||
44 | triggered upon idle states entry and exit. | ||
45 | |||
46 | The following diagram depicts the CPU execution phases and related timing | ||
47 | properties required to enter and exit an idle state: | ||
48 | |||
49 | ..__[EXEC]__|__[PREP]__|__[ENTRY]__|__[IDLE]__|__[EXIT]__|__[EXEC]__.. | ||
50 | | | | | | | ||
51 | |||
52 | |<------ entry ------->| | ||
53 | | latency | | ||
54 | |<- exit ->| | ||
55 | | latency | | ||
56 | |<-------- min-residency -------->| | ||
57 | |<------- wakeup-latency ------->| | ||
58 | |||
59 | Diagram 1: CPU idle state execution phases | ||
60 | |||
61 | EXEC: Normal CPU execution. | ||
62 | |||
63 | PREP: Preparation phase before committing the hardware to idle mode | ||
64 | like cache flushing. This is abortable on pending wake-up | ||
65 | event conditions. The abort latency is assumed to be negligible | ||
66 | (i.e. less than the ENTRY + EXIT duration). If aborted, CPU | ||
67 | goes back to EXEC. This phase is optional. If not abortable, | ||
68 | this should be included in the ENTRY phase instead. | ||
69 | |||
70 | ENTRY: The hardware is committed to idle mode. This period must run | ||
71 | to completion up to IDLE before anything else can happen. | ||
72 | |||
73 | IDLE: This is the actual energy-saving idle period. This may last | ||
74 | between 0 and infinite time, until a wake-up event occurs. | ||
75 | |||
76 | EXIT: Period during which the CPU is brought back to operational | ||
77 | mode (EXEC). | ||
78 | |||
79 | entry-latency: Worst case latency required to enter the idle state. The | ||
80 | exit-latency may be guaranteed only after entry-latency has passed. | ||
81 | |||
82 | min-residency: Minimum period, including preparation and entry, for a given | ||
83 | idle state to be worthwhile energywise. | ||
84 | |||
85 | wakeup-latency: Maximum delay between the signaling of a wake-up event and the | ||
86 | CPU being able to execute normal code again. If not specified, this is assumed | ||
87 | to be entry-latency + exit-latency. | ||
88 | |||
89 | These timing parameters can be used by an OS in different circumstances. | ||
90 | |||
91 | An idle CPU requires the expected min-residency time to select the most | ||
92 | appropriate idle state based on the expected expiry time of the next IRQ | ||
93 | (ie wake-up) that causes the CPU to return to the EXEC phase. | ||
94 | |||
95 | An operating system scheduler may need to compute the shortest wake-up delay | ||
96 | for CPUs in the system by detecting how long will it take to get a CPU out | ||
97 | of an idle state, eg: | ||
98 | |||
99 | wakeup-delay = exit-latency + max(entry-latency - (now - entry-timestamp), 0) | ||
100 | |||
101 | In other words, the scheduler can make its scheduling decision by selecting | ||
102 | (eg waking-up) the CPU with the shortest wake-up latency. | ||
103 | The wake-up latency must take into account the entry latency if that period | ||
104 | has not expired. The abortable nature of the PREP period can be ignored | ||
105 | if it cannot be relied upon (e.g. the PREP deadline may occur much sooner than | ||
106 | the worst case since it depends on the CPU operating conditions, ie caches | ||
107 | state). | ||
108 | |||
109 | An OS has to reliably probe the wakeup-latency since some devices can enforce | ||
110 | latency constraints guarantees to work properly, so the OS has to detect the | ||
111 | worst case wake-up latency it can incur if a CPU is allowed to enter an | ||
112 | idle state, and possibly to prevent that to guarantee reliable device | ||
113 | functioning. | ||
114 | |||
115 | The min-residency time parameter deserves further explanation since it is | ||
116 | expressed in time units but must factor in energy consumption coefficients. | ||
117 | |||
118 | The energy consumption of a cpu when it enters a power state can be roughly | ||
119 | characterised by the following graph: | ||
120 | |||
121 | | | ||
122 | | | ||
123 | | | ||
124 | e | | ||
125 | n | /--- | ||
126 | e | /------ | ||
127 | r | /------ | ||
128 | g | /----- | ||
129 | y | /------ | ||
130 | | ---- | ||
131 | | /| | ||
132 | | / | | ||
133 | | / | | ||
134 | | / | | ||
135 | | / | | ||
136 | | / | | ||
137 | |/ | | ||
138 | -----|-------+---------------------------------- | ||
139 | 0| 1 time(ms) | ||
140 | |||
141 | Graph 1: Energy vs time example | ||
142 | |||
143 | The graph is split in two parts delimited by time 1ms on the X-axis. | ||
144 | The graph curve with X-axis values = { x | 0 < x < 1ms } has a steep slope | ||
145 | and denotes the energy costs incurred whilst entering and leaving the idle | ||
146 | state. | ||
147 | The graph curve in the area delimited by X-axis values = {x | x > 1ms } has | ||
148 | shallower slope and essentially represents the energy consumption of the idle | ||
149 | state. | ||
150 | |||
151 | min-residency is defined for a given idle state as the minimum expected | ||
152 | residency time for a state (inclusive of preparation and entry) after | ||
153 | which choosing that state become the most energy efficient option. A good | ||
154 | way to visualise this, is by taking the same graph above and comparing some | ||
155 | states energy consumptions plots. | ||
156 | |||
157 | For sake of simplicity, let's consider a system with two idle states IDLE1, | ||
158 | and IDLE2: | ||
159 | |||
160 | | | ||
161 | | | ||
162 | | | ||
163 | | /-- IDLE1 | ||
164 | e | /--- | ||
165 | n | /---- | ||
166 | e | /--- | ||
167 | r | /-----/--------- IDLE2 | ||
168 | g | /-------/--------- | ||
169 | y | ------------ /---| | ||
170 | | / /---- | | ||
171 | | / /--- | | ||
172 | | / /---- | | ||
173 | | / /--- | | ||
174 | | --- | | ||
175 | | / | | ||
176 | | / | | ||
177 | |/ | time | ||
178 | ---/----------------------------+------------------------ | ||
179 | |IDLE1-energy < IDLE2-energy | IDLE2-energy < IDLE1-energy | ||
180 | | | ||
181 | IDLE2-min-residency | ||
182 | |||
183 | Graph 2: idle states min-residency example | ||
184 | |||
185 | In graph 2 above, that takes into account idle states entry/exit energy | ||
186 | costs, it is clear that if the idle state residency time (ie time till next | ||
187 | wake-up IRQ) is less than IDLE2-min-residency, IDLE1 is the better idle state | ||
188 | choice energywise. | ||
189 | |||
190 | This is mainly down to the fact that IDLE1 entry/exit energy costs are lower | ||
191 | than IDLE2. | ||
192 | |||
193 | However, the lower power consumption (ie shallower energy curve slope) of idle | ||
194 | state IDLE2 implies that after a suitable time, IDLE2 becomes more energy | ||
195 | efficient. | ||
196 | |||
197 | The time at which IDLE2 becomes more energy efficient than IDLE1 (and other | ||
198 | shallower states in a system with multiple idle states) is defined | ||
199 | IDLE2-min-residency and corresponds to the time when energy consumption of | ||
200 | IDLE1 and IDLE2 states breaks even. | ||
201 | |||
202 | The definitions provided in this section underpin the idle states | ||
203 | properties specification that is the subject of the following sections. | ||
204 | |||
205 | =========================================== | ||
206 | 3 - idle-states node | ||
207 | =========================================== | ||
208 | |||
209 | ARM processor idle states are defined within the idle-states node, which is | ||
210 | a direct child of the cpus node [1] and provides a container where the | ||
211 | processor idle states, defined as device tree nodes, are listed. | ||
212 | |||
213 | - idle-states node | ||
214 | |||
215 | Usage: Optional - On ARM systems, it is a container of processor idle | ||
216 | states nodes. If the system does not provide CPU | ||
217 | power management capabilities or the processor just | ||
218 | supports idle_standby an idle-states node is not | ||
219 | required. | ||
220 | |||
221 | Description: idle-states node is a container node, where its | ||
222 | subnodes describe the CPU idle states. | ||
223 | |||
224 | Node name must be "idle-states". | ||
225 | |||
226 | The idle-states node's parent node must be the cpus node. | ||
227 | |||
228 | The idle-states node's child nodes can be: | ||
229 | |||
230 | - one or more state nodes | ||
231 | |||
232 | Any other configuration is considered invalid. | ||
233 | |||
234 | An idle-states node defines the following properties: | ||
235 | |||
236 | - entry-method | ||
237 | Value type: <stringlist> | ||
238 | Usage and definition depend on ARM architecture version. | ||
239 | # On ARM v8 64-bit this property is required and must | ||
240 | be one of: | ||
241 | - "psci" (see bindings in [2]) | ||
242 | # On ARM 32-bit systems this property is optional | ||
243 | |||
244 | The nodes describing the idle states (state) can only be defined within the | ||
245 | idle-states node, any other configuration is considered invalid and therefore | ||
246 | must be ignored. | ||
247 | |||
248 | =========================================== | ||
249 | 4 - state node | ||
250 | =========================================== | ||
251 | |||
252 | A state node represents an idle state description and must be defined as | ||
253 | follows: | ||
254 | |||
255 | - state node | ||
256 | |||
257 | Description: must be child of the idle-states node | ||
258 | |||
259 | The state node name shall follow standard device tree naming | ||
260 | rules ([5], 2.2.1 "Node names"), in particular state nodes which | ||
261 | are siblings within a single common parent must be given a unique name. | ||
262 | |||
263 | The idle state entered by executing the wfi instruction (idle_standby | ||
264 | SBSA,[3][4]) is considered standard on all ARM platforms and therefore | ||
265 | must not be listed. | ||
266 | |||
267 | With the definitions provided above, the following list represents | ||
268 | the valid properties for a state node: | ||
269 | |||
270 | - compatible | ||
271 | Usage: Required | ||
272 | Value type: <stringlist> | ||
273 | Definition: Must be "arm,idle-state". | ||
274 | |||
275 | - local-timer-stop | ||
276 | Usage: See definition | ||
277 | Value type: <none> | ||
278 | Definition: if present the CPU local timer control logic is | ||
279 | lost on state entry, otherwise it is retained. | ||
280 | |||
281 | - entry-latency-us | ||
282 | Usage: Required | ||
283 | Value type: <prop-encoded-array> | ||
284 | Definition: u32 value representing worst case latency in | ||
285 | microseconds required to enter the idle state. | ||
286 | The exit-latency-us duration may be guaranteed | ||
287 | only after entry-latency-us has passed. | ||
288 | |||
289 | - exit-latency-us | ||
290 | Usage: Required | ||
291 | Value type: <prop-encoded-array> | ||
292 | Definition: u32 value representing worst case latency | ||
293 | in microseconds required to exit the idle state. | ||
294 | |||
295 | - min-residency-us | ||
296 | Usage: Required | ||
297 | Value type: <prop-encoded-array> | ||
298 | Definition: u32 value representing minimum residency duration | ||
299 | in microseconds, inclusive of preparation and | ||
300 | entry, for this idle state to be considered | ||
301 | worthwhile energy wise (refer to section 2 of | ||
302 | this document for a complete description). | ||
303 | |||
304 | - wakeup-latency-us: | ||
305 | Usage: Optional | ||
306 | Value type: <prop-encoded-array> | ||
307 | Definition: u32 value representing maximum delay between the | ||
308 | signaling of a wake-up event and the CPU being | ||
309 | able to execute normal code again. If omitted, | ||
310 | this is assumed to be equal to: | ||
311 | |||
312 | entry-latency-us + exit-latency-us | ||
313 | |||
314 | It is important to supply this value on systems | ||
315 | where the duration of PREP phase (see diagram 1, | ||
316 | section 2) is non-neglibigle. | ||
317 | In such systems entry-latency-us + exit-latency-us | ||
318 | will exceed wakeup-latency-us by this duration. | ||
319 | |||
320 | In addition to the properties listed above, a state node may require | ||
321 | additional properties specifics to the entry-method defined in the | ||
322 | idle-states node, please refer to the entry-method bindings | ||
323 | documentation for properties definitions. | ||
324 | |||
325 | =========================================== | ||
326 | 4 - Examples | ||
327 | =========================================== | ||
328 | |||
329 | Example 1 (ARM 64-bit, 16-cpu system, PSCI enable-method): | ||
330 | |||
331 | cpus { | ||
332 | #size-cells = <0>; | ||
333 | #address-cells = <2>; | ||
334 | |||
335 | CPU0: cpu@0 { | ||
336 | device_type = "cpu"; | ||
337 | compatible = "arm,cortex-a57"; | ||
338 | reg = <0x0 0x0>; | ||
339 | enable-method = "psci"; | ||
340 | cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 | ||
341 | &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; | ||
342 | }; | ||
343 | |||
344 | CPU1: cpu@1 { | ||
345 | device_type = "cpu"; | ||
346 | compatible = "arm,cortex-a57"; | ||
347 | reg = <0x0 0x1>; | ||
348 | enable-method = "psci"; | ||
349 | cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 | ||
350 | &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; | ||
351 | }; | ||
352 | |||
353 | CPU2: cpu@100 { | ||
354 | device_type = "cpu"; | ||
355 | compatible = "arm,cortex-a57"; | ||
356 | reg = <0x0 0x100>; | ||
357 | enable-method = "psci"; | ||
358 | cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 | ||
359 | &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; | ||
360 | }; | ||
361 | |||
362 | CPU3: cpu@101 { | ||
363 | device_type = "cpu"; | ||
364 | compatible = "arm,cortex-a57"; | ||
365 | reg = <0x0 0x101>; | ||
366 | enable-method = "psci"; | ||
367 | cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 | ||
368 | &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; | ||
369 | }; | ||
370 | |||
371 | CPU4: cpu@10000 { | ||
372 | device_type = "cpu"; | ||
373 | compatible = "arm,cortex-a57"; | ||
374 | reg = <0x0 0x10000>; | ||
375 | enable-method = "psci"; | ||
376 | cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 | ||
377 | &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; | ||
378 | }; | ||
379 | |||
380 | CPU5: cpu@10001 { | ||
381 | device_type = "cpu"; | ||
382 | compatible = "arm,cortex-a57"; | ||
383 | reg = <0x0 0x10001>; | ||
384 | enable-method = "psci"; | ||
385 | cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 | ||
386 | &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; | ||
387 | }; | ||
388 | |||
389 | CPU6: cpu@10100 { | ||
390 | device_type = "cpu"; | ||
391 | compatible = "arm,cortex-a57"; | ||
392 | reg = <0x0 0x10100>; | ||
393 | enable-method = "psci"; | ||
394 | cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 | ||
395 | &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; | ||
396 | }; | ||
397 | |||
398 | CPU7: cpu@10101 { | ||
399 | device_type = "cpu"; | ||
400 | compatible = "arm,cortex-a57"; | ||
401 | reg = <0x0 0x10101>; | ||
402 | enable-method = "psci"; | ||
403 | cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 | ||
404 | &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; | ||
405 | }; | ||
406 | |||
407 | CPU8: cpu@100000000 { | ||
408 | device_type = "cpu"; | ||
409 | compatible = "arm,cortex-a53"; | ||
410 | reg = <0x1 0x0>; | ||
411 | enable-method = "psci"; | ||
412 | cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 | ||
413 | &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; | ||
414 | }; | ||
415 | |||
416 | CPU9: cpu@100000001 { | ||
417 | device_type = "cpu"; | ||
418 | compatible = "arm,cortex-a53"; | ||
419 | reg = <0x1 0x1>; | ||
420 | enable-method = "psci"; | ||
421 | cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 | ||
422 | &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; | ||
423 | }; | ||
424 | |||
425 | CPU10: cpu@100000100 { | ||
426 | device_type = "cpu"; | ||
427 | compatible = "arm,cortex-a53"; | ||
428 | reg = <0x1 0x100>; | ||
429 | enable-method = "psci"; | ||
430 | cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 | ||
431 | &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; | ||
432 | }; | ||
433 | |||
434 | CPU11: cpu@100000101 { | ||
435 | device_type = "cpu"; | ||
436 | compatible = "arm,cortex-a53"; | ||
437 | reg = <0x1 0x101>; | ||
438 | enable-method = "psci"; | ||
439 | cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 | ||
440 | &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; | ||
441 | }; | ||
442 | |||
443 | CPU12: cpu@100010000 { | ||
444 | device_type = "cpu"; | ||
445 | compatible = "arm,cortex-a53"; | ||
446 | reg = <0x1 0x10000>; | ||
447 | enable-method = "psci"; | ||
448 | cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 | ||
449 | &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; | ||
450 | }; | ||
451 | |||
452 | CPU13: cpu@100010001 { | ||
453 | device_type = "cpu"; | ||
454 | compatible = "arm,cortex-a53"; | ||
455 | reg = <0x1 0x10001>; | ||
456 | enable-method = "psci"; | ||
457 | cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 | ||
458 | &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; | ||
459 | }; | ||
460 | |||
461 | CPU14: cpu@100010100 { | ||
462 | device_type = "cpu"; | ||
463 | compatible = "arm,cortex-a53"; | ||
464 | reg = <0x1 0x10100>; | ||
465 | enable-method = "psci"; | ||
466 | cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 | ||
467 | &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; | ||
468 | }; | ||
469 | |||
470 | CPU15: cpu@100010101 { | ||
471 | device_type = "cpu"; | ||
472 | compatible = "arm,cortex-a53"; | ||
473 | reg = <0x1 0x10101>; | ||
474 | enable-method = "psci"; | ||
475 | cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 | ||
476 | &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; | ||
477 | }; | ||
478 | |||
479 | idle-states { | ||
480 | entry-method = "arm,psci"; | ||
481 | |||
482 | CPU_RETENTION_0_0: cpu-retention-0-0 { | ||
483 | compatible = "arm,idle-state"; | ||
484 | arm,psci-suspend-param = <0x0010000>; | ||
485 | entry-latency-us = <20>; | ||
486 | exit-latency-us = <40>; | ||
487 | min-residency-us = <80>; | ||
488 | }; | ||
489 | |||
490 | CLUSTER_RETENTION_0: cluster-retention-0 { | ||
491 | compatible = "arm,idle-state"; | ||
492 | local-timer-stop; | ||
493 | arm,psci-suspend-param = <0x1010000>; | ||
494 | entry-latency-us = <50>; | ||
495 | exit-latency-us = <100>; | ||
496 | min-residency-us = <250>; | ||
497 | wakeup-latency-us = <130>; | ||
498 | }; | ||
499 | |||
500 | CPU_SLEEP_0_0: cpu-sleep-0-0 { | ||
501 | compatible = "arm,idle-state"; | ||
502 | local-timer-stop; | ||
503 | arm,psci-suspend-param = <0x0010000>; | ||
504 | entry-latency-us = <250>; | ||
505 | exit-latency-us = <500>; | ||
506 | min-residency-us = <950>; | ||
507 | }; | ||
508 | |||
509 | CLUSTER_SLEEP_0: cluster-sleep-0 { | ||
510 | compatible = "arm,idle-state"; | ||
511 | local-timer-stop; | ||
512 | arm,psci-suspend-param = <0x1010000>; | ||
513 | entry-latency-us = <600>; | ||
514 | exit-latency-us = <1100>; | ||
515 | min-residency-us = <2700>; | ||
516 | wakeup-latency-us = <1500>; | ||
517 | }; | ||
518 | |||
519 | CPU_RETENTION_1_0: cpu-retention-1-0 { | ||
520 | compatible = "arm,idle-state"; | ||
521 | arm,psci-suspend-param = <0x0010000>; | ||
522 | entry-latency-us = <20>; | ||
523 | exit-latency-us = <40>; | ||
524 | min-residency-us = <90>; | ||
525 | }; | ||
526 | |||
527 | CLUSTER_RETENTION_1: cluster-retention-1 { | ||
528 | compatible = "arm,idle-state"; | ||
529 | local-timer-stop; | ||
530 | arm,psci-suspend-param = <0x1010000>; | ||
531 | entry-latency-us = <50>; | ||
532 | exit-latency-us = <100>; | ||
533 | min-residency-us = <270>; | ||
534 | wakeup-latency-us = <100>; | ||
535 | }; | ||
536 | |||
537 | CPU_SLEEP_1_0: cpu-sleep-1-0 { | ||
538 | compatible = "arm,idle-state"; | ||
539 | local-timer-stop; | ||
540 | arm,psci-suspend-param = <0x0010000>; | ||
541 | entry-latency-us = <70>; | ||
542 | exit-latency-us = <100>; | ||
543 | min-residency-us = <300>; | ||
544 | wakeup-latency-us = <150>; | ||
545 | }; | ||
546 | |||
547 | CLUSTER_SLEEP_1: cluster-sleep-1 { | ||
548 | compatible = "arm,idle-state"; | ||
549 | local-timer-stop; | ||
550 | arm,psci-suspend-param = <0x1010000>; | ||
551 | entry-latency-us = <500>; | ||
552 | exit-latency-us = <1200>; | ||
553 | min-residency-us = <3500>; | ||
554 | wakeup-latency-us = <1300>; | ||
555 | }; | ||
556 | }; | ||
557 | |||
558 | }; | ||
559 | |||
560 | Example 2 (ARM 32-bit, 8-cpu system, two clusters): | ||
561 | |||
562 | cpus { | ||
563 | #size-cells = <0>; | ||
564 | #address-cells = <1>; | ||
565 | |||
566 | CPU0: cpu@0 { | ||
567 | device_type = "cpu"; | ||
568 | compatible = "arm,cortex-a15"; | ||
569 | reg = <0x0>; | ||
570 | cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>; | ||
571 | }; | ||
572 | |||
573 | CPU1: cpu@1 { | ||
574 | device_type = "cpu"; | ||
575 | compatible = "arm,cortex-a15"; | ||
576 | reg = <0x1>; | ||
577 | cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>; | ||
578 | }; | ||
579 | |||
580 | CPU2: cpu@2 { | ||
581 | device_type = "cpu"; | ||
582 | compatible = "arm,cortex-a15"; | ||
583 | reg = <0x2>; | ||
584 | cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>; | ||
585 | }; | ||
586 | |||
587 | CPU3: cpu@3 { | ||
588 | device_type = "cpu"; | ||
589 | compatible = "arm,cortex-a15"; | ||
590 | reg = <0x3>; | ||
591 | cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>; | ||
592 | }; | ||
593 | |||
594 | CPU4: cpu@100 { | ||
595 | device_type = "cpu"; | ||
596 | compatible = "arm,cortex-a7"; | ||
597 | reg = <0x100>; | ||
598 | cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>; | ||
599 | }; | ||
600 | |||
601 | CPU5: cpu@101 { | ||
602 | device_type = "cpu"; | ||
603 | compatible = "arm,cortex-a7"; | ||
604 | reg = <0x101>; | ||
605 | cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>; | ||
606 | }; | ||
607 | |||
608 | CPU6: cpu@102 { | ||
609 | device_type = "cpu"; | ||
610 | compatible = "arm,cortex-a7"; | ||
611 | reg = <0x102>; | ||
612 | cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>; | ||
613 | }; | ||
614 | |||
615 | CPU7: cpu@103 { | ||
616 | device_type = "cpu"; | ||
617 | compatible = "arm,cortex-a7"; | ||
618 | reg = <0x103>; | ||
619 | cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>; | ||
620 | }; | ||
621 | |||
622 | idle-states { | ||
623 | CPU_SLEEP_0_0: cpu-sleep-0-0 { | ||
624 | compatible = "arm,idle-state"; | ||
625 | local-timer-stop; | ||
626 | entry-latency-us = <200>; | ||
627 | exit-latency-us = <100>; | ||
628 | min-residency-us = <400>; | ||
629 | wakeup-latency-us = <250>; | ||
630 | }; | ||
631 | |||
632 | CLUSTER_SLEEP_0: cluster-sleep-0 { | ||
633 | compatible = "arm,idle-state"; | ||
634 | local-timer-stop; | ||
635 | entry-latency-us = <500>; | ||
636 | exit-latency-us = <1500>; | ||
637 | min-residency-us = <2500>; | ||
638 | wakeup-latency-us = <1700>; | ||
639 | }; | ||
640 | |||
641 | CPU_SLEEP_1_0: cpu-sleep-1-0 { | ||
642 | compatible = "arm,idle-state"; | ||
643 | local-timer-stop; | ||
644 | entry-latency-us = <300>; | ||
645 | exit-latency-us = <500>; | ||
646 | min-residency-us = <900>; | ||
647 | wakeup-latency-us = <600>; | ||
648 | }; | ||
649 | |||
650 | CLUSTER_SLEEP_1: cluster-sleep-1 { | ||
651 | compatible = "arm,idle-state"; | ||
652 | local-timer-stop; | ||
653 | entry-latency-us = <800>; | ||
654 | exit-latency-us = <2000>; | ||
655 | min-residency-us = <6500>; | ||
656 | wakeup-latency-us = <2300>; | ||
657 | }; | ||
658 | }; | ||
659 | |||
660 | }; | ||
661 | |||
662 | =========================================== | ||
663 | 5 - References | ||
664 | =========================================== | ||
665 | |||
666 | [1] ARM Linux Kernel documentation - CPUs bindings | ||
667 | Documentation/devicetree/bindings/arm/cpus.txt | ||
668 | |||
669 | [2] ARM Linux Kernel documentation - PSCI bindings | ||
670 | Documentation/devicetree/bindings/arm/psci.txt | ||
671 | |||
672 | [3] ARM Server Base System Architecture (SBSA) | ||
673 | http://infocenter.arm.com/help/index.jsp | ||
674 | |||
675 | [4] ARM Architecture Reference Manuals | ||
676 | http://infocenter.arm.com/help/index.jsp | ||
677 | |||
678 | [5] ePAPR standard | ||
679 | https://www.power.org/documentation/epapr-version-1-1/ | ||
diff --git a/Documentation/devicetree/bindings/arm/psci.txt b/Documentation/devicetree/bindings/arm/psci.txt index b4a58f39223c..5aa40ede0e99 100644 --- a/Documentation/devicetree/bindings/arm/psci.txt +++ b/Documentation/devicetree/bindings/arm/psci.txt | |||
@@ -50,6 +50,16 @@ Main node optional properties: | |||
50 | 50 | ||
51 | - migrate : Function ID for MIGRATE operation | 51 | - migrate : Function ID for MIGRATE operation |
52 | 52 | ||
53 | Device tree nodes that require usage of PSCI CPU_SUSPEND function (ie idle | ||
54 | state nodes, as per bindings in [1]) must specify the following properties: | ||
55 | |||
56 | - arm,psci-suspend-param | ||
57 | Usage: Required for state nodes[1] if the corresponding | ||
58 | idle-states node entry-method property is set | ||
59 | to "psci". | ||
60 | Value type: <u32> | ||
61 | Definition: power_state parameter to pass to the PSCI | ||
62 | suspend call. | ||
53 | 63 | ||
54 | Example: | 64 | Example: |
55 | 65 | ||
@@ -64,7 +74,6 @@ Case 1: PSCI v0.1 only. | |||
64 | migrate = <0x95c10003>; | 74 | migrate = <0x95c10003>; |
65 | }; | 75 | }; |
66 | 76 | ||
67 | |||
68 | Case 2: PSCI v0.2 only | 77 | Case 2: PSCI v0.2 only |
69 | 78 | ||
70 | psci { | 79 | psci { |
@@ -88,3 +97,6 @@ Case 3: PSCI v0.2 and PSCI v0.1. | |||
88 | 97 | ||
89 | ... | 98 | ... |
90 | }; | 99 | }; |
100 | |||
101 | [1] Kernel documentation - ARM idle states bindings | ||
102 | Documentation/devicetree/bindings/arm/idle-states.txt | ||
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts index a25c262326dc..322fd1519b09 100644 --- a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts +++ b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts | |||
@@ -38,6 +38,7 @@ | |||
38 | compatible = "arm,cortex-a15"; | 38 | compatible = "arm,cortex-a15"; |
39 | reg = <0>; | 39 | reg = <0>; |
40 | cci-control-port = <&cci_control1>; | 40 | cci-control-port = <&cci_control1>; |
41 | cpu-idle-states = <&CLUSTER_SLEEP_BIG>; | ||
41 | }; | 42 | }; |
42 | 43 | ||
43 | cpu1: cpu@1 { | 44 | cpu1: cpu@1 { |
@@ -45,6 +46,7 @@ | |||
45 | compatible = "arm,cortex-a15"; | 46 | compatible = "arm,cortex-a15"; |
46 | reg = <1>; | 47 | reg = <1>; |
47 | cci-control-port = <&cci_control1>; | 48 | cci-control-port = <&cci_control1>; |
49 | cpu-idle-states = <&CLUSTER_SLEEP_BIG>; | ||
48 | }; | 50 | }; |
49 | 51 | ||
50 | cpu2: cpu@2 { | 52 | cpu2: cpu@2 { |
@@ -52,6 +54,7 @@ | |||
52 | compatible = "arm,cortex-a7"; | 54 | compatible = "arm,cortex-a7"; |
53 | reg = <0x100>; | 55 | reg = <0x100>; |
54 | cci-control-port = <&cci_control2>; | 56 | cci-control-port = <&cci_control2>; |
57 | cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>; | ||
55 | }; | 58 | }; |
56 | 59 | ||
57 | cpu3: cpu@3 { | 60 | cpu3: cpu@3 { |
@@ -59,6 +62,7 @@ | |||
59 | compatible = "arm,cortex-a7"; | 62 | compatible = "arm,cortex-a7"; |
60 | reg = <0x101>; | 63 | reg = <0x101>; |
61 | cci-control-port = <&cci_control2>; | 64 | cci-control-port = <&cci_control2>; |
65 | cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>; | ||
62 | }; | 66 | }; |
63 | 67 | ||
64 | cpu4: cpu@4 { | 68 | cpu4: cpu@4 { |
@@ -66,6 +70,25 @@ | |||
66 | compatible = "arm,cortex-a7"; | 70 | compatible = "arm,cortex-a7"; |
67 | reg = <0x102>; | 71 | reg = <0x102>; |
68 | cci-control-port = <&cci_control2>; | 72 | cci-control-port = <&cci_control2>; |
73 | cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>; | ||
74 | }; | ||
75 | |||
76 | idle-states { | ||
77 | CLUSTER_SLEEP_BIG: cluster-sleep-big { | ||
78 | compatible = "arm,idle-state"; | ||
79 | local-timer-stop; | ||
80 | entry-latency-us = <1000>; | ||
81 | exit-latency-us = <700>; | ||
82 | min-residency-us = <2000>; | ||
83 | }; | ||
84 | |||
85 | CLUSTER_SLEEP_LITTLE: cluster-sleep-little { | ||
86 | compatible = "arm,idle-state"; | ||
87 | local-timer-stop; | ||
88 | entry-latency-us = <1000>; | ||
89 | exit-latency-us = <500>; | ||
90 | min-residency-us = <2500>; | ||
91 | }; | ||
69 | }; | 92 | }; |
70 | }; | 93 | }; |
71 | 94 | ||
diff --git a/arch/arm64/include/asm/cpu_ops.h b/arch/arm64/include/asm/cpu_ops.h index d7b4b38a8e86..47dfa31ad71a 100644 --- a/arch/arm64/include/asm/cpu_ops.h +++ b/arch/arm64/include/asm/cpu_ops.h | |||
@@ -28,6 +28,8 @@ struct device_node; | |||
28 | * enable-method property. | 28 | * enable-method property. |
29 | * @cpu_init: Reads any data necessary for a specific enable-method from the | 29 | * @cpu_init: Reads any data necessary for a specific enable-method from the |
30 | * devicetree, for a given cpu node and proposed logical id. | 30 | * devicetree, for a given cpu node and proposed logical id. |
31 | * @cpu_init_idle: Reads any data necessary to initialize CPU idle states from | ||
32 | * devicetree, for a given cpu node and proposed logical id. | ||
31 | * @cpu_prepare: Early one-time preparation step for a cpu. If there is a | 33 | * @cpu_prepare: Early one-time preparation step for a cpu. If there is a |
32 | * mechanism for doing so, tests whether it is possible to boot | 34 | * mechanism for doing so, tests whether it is possible to boot |
33 | * the given CPU. | 35 | * the given CPU. |
@@ -47,6 +49,7 @@ struct device_node; | |||
47 | struct cpu_operations { | 49 | struct cpu_operations { |
48 | const char *name; | 50 | const char *name; |
49 | int (*cpu_init)(struct device_node *, unsigned int); | 51 | int (*cpu_init)(struct device_node *, unsigned int); |
52 | int (*cpu_init_idle)(struct device_node *, unsigned int); | ||
50 | int (*cpu_prepare)(unsigned int); | 53 | int (*cpu_prepare)(unsigned int); |
51 | int (*cpu_boot)(unsigned int); | 54 | int (*cpu_boot)(unsigned int); |
52 | void (*cpu_postboot)(void); | 55 | void (*cpu_postboot)(void); |
diff --git a/arch/arm64/include/asm/cpuidle.h b/arch/arm64/include/asm/cpuidle.h new file mode 100644 index 000000000000..b52a9932e2b1 --- /dev/null +++ b/arch/arm64/include/asm/cpuidle.h | |||
@@ -0,0 +1,13 @@ | |||
1 | #ifndef __ASM_CPUIDLE_H | ||
2 | #define __ASM_CPUIDLE_H | ||
3 | |||
4 | #ifdef CONFIG_CPU_IDLE | ||
5 | extern int cpu_init_idle(unsigned int cpu); | ||
6 | #else | ||
7 | static inline int cpu_init_idle(unsigned int cpu) | ||
8 | { | ||
9 | return -EOPNOTSUPP; | ||
10 | } | ||
11 | #endif | ||
12 | |||
13 | #endif | ||
diff --git a/arch/arm64/include/asm/suspend.h b/arch/arm64/include/asm/suspend.h index e9c149c042e0..456d67c1f0fa 100644 --- a/arch/arm64/include/asm/suspend.h +++ b/arch/arm64/include/asm/suspend.h | |||
@@ -21,6 +21,7 @@ struct sleep_save_sp { | |||
21 | phys_addr_t save_ptr_stash_phys; | 21 | phys_addr_t save_ptr_stash_phys; |
22 | }; | 22 | }; |
23 | 23 | ||
24 | extern int __cpu_suspend(unsigned long arg, int (*fn)(unsigned long)); | ||
24 | extern void cpu_resume(void); | 25 | extern void cpu_resume(void); |
25 | extern int cpu_suspend(unsigned long); | 26 | extern int cpu_suspend(unsigned long); |
26 | 27 | ||
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index df7ef8768fc2..6e9538c2d28a 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile | |||
@@ -26,6 +26,7 @@ arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o | |||
26 | arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o | 26 | arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o |
27 | arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o | 27 | arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o |
28 | arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o | 28 | arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o |
29 | arm64-obj-$(CONFIG_CPU_IDLE) += cpuidle.o | ||
29 | arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o | 30 | arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o |
30 | arm64-obj-$(CONFIG_KGDB) += kgdb.o | 31 | arm64-obj-$(CONFIG_KGDB) += kgdb.o |
31 | arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o | 32 | arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o |
diff --git a/arch/arm64/kernel/cpuidle.c b/arch/arm64/kernel/cpuidle.c new file mode 100644 index 000000000000..19d17f51db37 --- /dev/null +++ b/arch/arm64/kernel/cpuidle.c | |||
@@ -0,0 +1,31 @@ | |||
1 | /* | ||
2 | * ARM64 CPU idle arch support | ||
3 | * | ||
4 | * Copyright (C) 2014 ARM Ltd. | ||
5 | * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/of.h> | ||
13 | #include <linux/of_device.h> | ||
14 | |||
15 | #include <asm/cpuidle.h> | ||
16 | #include <asm/cpu_ops.h> | ||
17 | |||
18 | int cpu_init_idle(unsigned int cpu) | ||
19 | { | ||
20 | int ret = -EOPNOTSUPP; | ||
21 | struct device_node *cpu_node = of_cpu_device_node_get(cpu); | ||
22 | |||
23 | if (!cpu_node) | ||
24 | return -ENODEV; | ||
25 | |||
26 | if (cpu_ops[cpu] && cpu_ops[cpu]->cpu_init_idle) | ||
27 | ret = cpu_ops[cpu]->cpu_init_idle(cpu_node, cpu); | ||
28 | |||
29 | of_node_put(cpu_node); | ||
30 | return ret; | ||
31 | } | ||
diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c index 553954771a67..866c1c821860 100644 --- a/arch/arm64/kernel/psci.c +++ b/arch/arm64/kernel/psci.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/reboot.h> | 21 | #include <linux/reboot.h> |
22 | #include <linux/pm.h> | 22 | #include <linux/pm.h> |
23 | #include <linux/delay.h> | 23 | #include <linux/delay.h> |
24 | #include <linux/slab.h> | ||
24 | #include <uapi/linux/psci.h> | 25 | #include <uapi/linux/psci.h> |
25 | 26 | ||
26 | #include <asm/compiler.h> | 27 | #include <asm/compiler.h> |
@@ -28,6 +29,7 @@ | |||
28 | #include <asm/errno.h> | 29 | #include <asm/errno.h> |
29 | #include <asm/psci.h> | 30 | #include <asm/psci.h> |
30 | #include <asm/smp_plat.h> | 31 | #include <asm/smp_plat.h> |
32 | #include <asm/suspend.h> | ||
31 | #include <asm/system_misc.h> | 33 | #include <asm/system_misc.h> |
32 | 34 | ||
33 | #define PSCI_POWER_STATE_TYPE_STANDBY 0 | 35 | #define PSCI_POWER_STATE_TYPE_STANDBY 0 |
@@ -65,6 +67,8 @@ enum psci_function { | |||
65 | PSCI_FN_MAX, | 67 | PSCI_FN_MAX, |
66 | }; | 68 | }; |
67 | 69 | ||
70 | static DEFINE_PER_CPU_READ_MOSTLY(struct psci_power_state *, psci_power_state); | ||
71 | |||
68 | static u32 psci_function_id[PSCI_FN_MAX]; | 72 | static u32 psci_function_id[PSCI_FN_MAX]; |
69 | 73 | ||
70 | static int psci_to_linux_errno(int errno) | 74 | static int psci_to_linux_errno(int errno) |
@@ -93,6 +97,18 @@ static u32 psci_power_state_pack(struct psci_power_state state) | |||
93 | & PSCI_0_2_POWER_STATE_AFFL_MASK); | 97 | & PSCI_0_2_POWER_STATE_AFFL_MASK); |
94 | } | 98 | } |
95 | 99 | ||
100 | static void psci_power_state_unpack(u32 power_state, | ||
101 | struct psci_power_state *state) | ||
102 | { | ||
103 | state->id = (power_state & PSCI_0_2_POWER_STATE_ID_MASK) >> | ||
104 | PSCI_0_2_POWER_STATE_ID_SHIFT; | ||
105 | state->type = (power_state & PSCI_0_2_POWER_STATE_TYPE_MASK) >> | ||
106 | PSCI_0_2_POWER_STATE_TYPE_SHIFT; | ||
107 | state->affinity_level = | ||
108 | (power_state & PSCI_0_2_POWER_STATE_AFFL_MASK) >> | ||
109 | PSCI_0_2_POWER_STATE_AFFL_SHIFT; | ||
110 | } | ||
111 | |||
96 | /* | 112 | /* |
97 | * The following two functions are invoked via the invoke_psci_fn pointer | 113 | * The following two functions are invoked via the invoke_psci_fn pointer |
98 | * and will not be inlined, allowing us to piggyback on the AAPCS. | 114 | * and will not be inlined, allowing us to piggyback on the AAPCS. |
@@ -199,6 +215,63 @@ static int psci_migrate_info_type(void) | |||
199 | return err; | 215 | return err; |
200 | } | 216 | } |
201 | 217 | ||
218 | static int __maybe_unused cpu_psci_cpu_init_idle(struct device_node *cpu_node, | ||
219 | unsigned int cpu) | ||
220 | { | ||
221 | int i, ret, count = 0; | ||
222 | struct psci_power_state *psci_states; | ||
223 | struct device_node *state_node; | ||
224 | |||
225 | /* | ||
226 | * If the PSCI cpu_suspend function hook has not been initialized | ||
227 | * idle states must not be enabled, so bail out | ||
228 | */ | ||
229 | if (!psci_ops.cpu_suspend) | ||
230 | return -EOPNOTSUPP; | ||
231 | |||
232 | /* Count idle states */ | ||
233 | while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states", | ||
234 | count))) { | ||
235 | count++; | ||
236 | of_node_put(state_node); | ||
237 | } | ||
238 | |||
239 | if (!count) | ||
240 | return -ENODEV; | ||
241 | |||
242 | psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL); | ||
243 | if (!psci_states) | ||
244 | return -ENOMEM; | ||
245 | |||
246 | for (i = 0; i < count; i++) { | ||
247 | u32 psci_power_state; | ||
248 | |||
249 | state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); | ||
250 | |||
251 | ret = of_property_read_u32(state_node, | ||
252 | "arm,psci-suspend-param", | ||
253 | &psci_power_state); | ||
254 | if (ret) { | ||
255 | pr_warn(" * %s missing arm,psci-suspend-param property\n", | ||
256 | state_node->full_name); | ||
257 | of_node_put(state_node); | ||
258 | goto free_mem; | ||
259 | } | ||
260 | |||
261 | of_node_put(state_node); | ||
262 | pr_debug("psci-power-state %#x index %d\n", psci_power_state, | ||
263 | i); | ||
264 | psci_power_state_unpack(psci_power_state, &psci_states[i]); | ||
265 | } | ||
266 | /* Idle states parsed correctly, initialize per-cpu pointer */ | ||
267 | per_cpu(psci_power_state, cpu) = psci_states; | ||
268 | return 0; | ||
269 | |||
270 | free_mem: | ||
271 | kfree(psci_states); | ||
272 | return ret; | ||
273 | } | ||
274 | |||
202 | static int get_set_conduit_method(struct device_node *np) | 275 | static int get_set_conduit_method(struct device_node *np) |
203 | { | 276 | { |
204 | const char *method; | 277 | const char *method; |
@@ -436,8 +509,39 @@ static int cpu_psci_cpu_kill(unsigned int cpu) | |||
436 | #endif | 509 | #endif |
437 | #endif | 510 | #endif |
438 | 511 | ||
512 | static int psci_suspend_finisher(unsigned long index) | ||
513 | { | ||
514 | struct psci_power_state *state = __get_cpu_var(psci_power_state); | ||
515 | |||
516 | return psci_ops.cpu_suspend(state[index - 1], | ||
517 | virt_to_phys(cpu_resume)); | ||
518 | } | ||
519 | |||
520 | static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index) | ||
521 | { | ||
522 | int ret; | ||
523 | struct psci_power_state *state = __get_cpu_var(psci_power_state); | ||
524 | /* | ||
525 | * idle state index 0 corresponds to wfi, should never be called | ||
526 | * from the cpu_suspend operations | ||
527 | */ | ||
528 | if (WARN_ON_ONCE(!index)) | ||
529 | return -EINVAL; | ||
530 | |||
531 | if (state->type == PSCI_POWER_STATE_TYPE_STANDBY) | ||
532 | ret = psci_ops.cpu_suspend(state[index - 1], 0); | ||
533 | else | ||
534 | ret = __cpu_suspend(index, psci_suspend_finisher); | ||
535 | |||
536 | return ret; | ||
537 | } | ||
538 | |||
439 | const struct cpu_operations cpu_psci_ops = { | 539 | const struct cpu_operations cpu_psci_ops = { |
440 | .name = "psci", | 540 | .name = "psci", |
541 | #ifdef CONFIG_CPU_IDLE | ||
542 | .cpu_init_idle = cpu_psci_cpu_init_idle, | ||
543 | .cpu_suspend = cpu_psci_cpu_suspend, | ||
544 | #endif | ||
441 | #ifdef CONFIG_SMP | 545 | #ifdef CONFIG_SMP |
442 | .cpu_init = cpu_psci_cpu_init, | 546 | .cpu_init = cpu_psci_cpu_init, |
443 | .cpu_prepare = cpu_psci_cpu_prepare, | 547 | .cpu_prepare = cpu_psci_cpu_prepare, |
diff --git a/arch/arm64/kernel/sleep.S b/arch/arm64/kernel/sleep.S index b1925729c692..a564b440416a 100644 --- a/arch/arm64/kernel/sleep.S +++ b/arch/arm64/kernel/sleep.S | |||
@@ -49,28 +49,39 @@ | |||
49 | orr \dst, \dst, \mask // dst|=(aff3>>rs3) | 49 | orr \dst, \dst, \mask // dst|=(aff3>>rs3) |
50 | .endm | 50 | .endm |
51 | /* | 51 | /* |
52 | * Save CPU state for a suspend. This saves callee registers, and allocates | 52 | * Save CPU state for a suspend and execute the suspend finisher. |
53 | * space on the kernel stack to save the CPU specific registers + some | 53 | * On success it will return 0 through cpu_resume - ie through a CPU |
54 | * other data for resume. | 54 | * soft/hard reboot from the reset vector. |
55 | * On failure it returns the suspend finisher return value or force | ||
56 | * -EOPNOTSUPP if the finisher erroneously returns 0 (the suspend finisher | ||
57 | * is not allowed to return, if it does this must be considered failure). | ||
58 | * It saves callee registers, and allocates space on the kernel stack | ||
59 | * to save the CPU specific registers + some other data for resume. | ||
55 | * | 60 | * |
56 | * x0 = suspend finisher argument | 61 | * x0 = suspend finisher argument |
62 | * x1 = suspend finisher function pointer | ||
57 | */ | 63 | */ |
58 | ENTRY(__cpu_suspend) | 64 | ENTRY(__cpu_suspend_enter) |
59 | stp x29, lr, [sp, #-96]! | 65 | stp x29, lr, [sp, #-96]! |
60 | stp x19, x20, [sp,#16] | 66 | stp x19, x20, [sp,#16] |
61 | stp x21, x22, [sp,#32] | 67 | stp x21, x22, [sp,#32] |
62 | stp x23, x24, [sp,#48] | 68 | stp x23, x24, [sp,#48] |
63 | stp x25, x26, [sp,#64] | 69 | stp x25, x26, [sp,#64] |
64 | stp x27, x28, [sp,#80] | 70 | stp x27, x28, [sp,#80] |
71 | /* | ||
72 | * Stash suspend finisher and its argument in x20 and x19 | ||
73 | */ | ||
74 | mov x19, x0 | ||
75 | mov x20, x1 | ||
65 | mov x2, sp | 76 | mov x2, sp |
66 | sub sp, sp, #CPU_SUSPEND_SZ // allocate cpu_suspend_ctx | 77 | sub sp, sp, #CPU_SUSPEND_SZ // allocate cpu_suspend_ctx |
67 | mov x1, sp | 78 | mov x0, sp |
68 | /* | 79 | /* |
69 | * x1 now points to struct cpu_suspend_ctx allocated on the stack | 80 | * x0 now points to struct cpu_suspend_ctx allocated on the stack |
70 | */ | 81 | */ |
71 | str x2, [x1, #CPU_CTX_SP] | 82 | str x2, [x0, #CPU_CTX_SP] |
72 | ldr x2, =sleep_save_sp | 83 | ldr x1, =sleep_save_sp |
73 | ldr x2, [x2, #SLEEP_SAVE_SP_VIRT] | 84 | ldr x1, [x1, #SLEEP_SAVE_SP_VIRT] |
74 | #ifdef CONFIG_SMP | 85 | #ifdef CONFIG_SMP |
75 | mrs x7, mpidr_el1 | 86 | mrs x7, mpidr_el1 |
76 | ldr x9, =mpidr_hash | 87 | ldr x9, =mpidr_hash |
@@ -82,11 +93,21 @@ ENTRY(__cpu_suspend) | |||
82 | ldp w3, w4, [x9, #MPIDR_HASH_SHIFTS] | 93 | ldp w3, w4, [x9, #MPIDR_HASH_SHIFTS] |
83 | ldp w5, w6, [x9, #(MPIDR_HASH_SHIFTS + 8)] | 94 | ldp w5, w6, [x9, #(MPIDR_HASH_SHIFTS + 8)] |
84 | compute_mpidr_hash x8, x3, x4, x5, x6, x7, x10 | 95 | compute_mpidr_hash x8, x3, x4, x5, x6, x7, x10 |
85 | add x2, x2, x8, lsl #3 | 96 | add x1, x1, x8, lsl #3 |
86 | #endif | 97 | #endif |
87 | bl __cpu_suspend_finisher | 98 | bl __cpu_suspend_save |
99 | /* | ||
100 | * Grab suspend finisher in x20 and its argument in x19 | ||
101 | */ | ||
102 | mov x0, x19 | ||
103 | mov x1, x20 | ||
104 | /* | ||
105 | * We are ready for power down, fire off the suspend finisher | ||
106 | * in x1, with argument in x0 | ||
107 | */ | ||
108 | blr x1 | ||
88 | /* | 109 | /* |
89 | * Never gets here, unless suspend fails. | 110 | * Never gets here, unless suspend finisher fails. |
90 | * Successful cpu_suspend should return from cpu_resume, returning | 111 | * Successful cpu_suspend should return from cpu_resume, returning |
91 | * through this code path is considered an error | 112 | * through this code path is considered an error |
92 | * If the return value is set to 0 force x0 = -EOPNOTSUPP | 113 | * If the return value is set to 0 force x0 = -EOPNOTSUPP |
@@ -103,7 +124,7 @@ ENTRY(__cpu_suspend) | |||
103 | ldp x27, x28, [sp, #80] | 124 | ldp x27, x28, [sp, #80] |
104 | ldp x29, lr, [sp], #96 | 125 | ldp x29, lr, [sp], #96 |
105 | ret | 126 | ret |
106 | ENDPROC(__cpu_suspend) | 127 | ENDPROC(__cpu_suspend_enter) |
107 | .ltorg | 128 | .ltorg |
108 | 129 | ||
109 | /* | 130 | /* |
diff --git a/arch/arm64/kernel/suspend.c b/arch/arm64/kernel/suspend.c index 55a99b9a97e0..13ad4dbb1615 100644 --- a/arch/arm64/kernel/suspend.c +++ b/arch/arm64/kernel/suspend.c | |||
@@ -9,22 +9,19 @@ | |||
9 | #include <asm/suspend.h> | 9 | #include <asm/suspend.h> |
10 | #include <asm/tlbflush.h> | 10 | #include <asm/tlbflush.h> |
11 | 11 | ||
12 | extern int __cpu_suspend(unsigned long); | 12 | extern int __cpu_suspend_enter(unsigned long arg, int (*fn)(unsigned long)); |
13 | /* | 13 | /* |
14 | * This is called by __cpu_suspend() to save the state, and do whatever | 14 | * This is called by __cpu_suspend_enter() to save the state, and do whatever |
15 | * flushing is required to ensure that when the CPU goes to sleep we have | 15 | * flushing is required to ensure that when the CPU goes to sleep we have |
16 | * the necessary data available when the caches are not searched. | 16 | * the necessary data available when the caches are not searched. |
17 | * | 17 | * |
18 | * @arg: Argument to pass to suspend operations | 18 | * ptr: CPU context virtual address |
19 | * @ptr: CPU context virtual address | 19 | * save_ptr: address of the location where the context physical address |
20 | * @save_ptr: address of the location where the context physical address | 20 | * must be saved |
21 | * must be saved | ||
22 | */ | 21 | */ |
23 | int __cpu_suspend_finisher(unsigned long arg, struct cpu_suspend_ctx *ptr, | 22 | void notrace __cpu_suspend_save(struct cpu_suspend_ctx *ptr, |
24 | phys_addr_t *save_ptr) | 23 | phys_addr_t *save_ptr) |
25 | { | 24 | { |
26 | int cpu = smp_processor_id(); | ||
27 | |||
28 | *save_ptr = virt_to_phys(ptr); | 25 | *save_ptr = virt_to_phys(ptr); |
29 | 26 | ||
30 | cpu_do_suspend(ptr); | 27 | cpu_do_suspend(ptr); |
@@ -35,8 +32,6 @@ int __cpu_suspend_finisher(unsigned long arg, struct cpu_suspend_ctx *ptr, | |||
35 | */ | 32 | */ |
36 | __flush_dcache_area(ptr, sizeof(*ptr)); | 33 | __flush_dcache_area(ptr, sizeof(*ptr)); |
37 | __flush_dcache_area(save_ptr, sizeof(*save_ptr)); | 34 | __flush_dcache_area(save_ptr, sizeof(*save_ptr)); |
38 | |||
39 | return cpu_ops[cpu]->cpu_suspend(arg); | ||
40 | } | 35 | } |
41 | 36 | ||
42 | /* | 37 | /* |
@@ -56,15 +51,15 @@ void __init cpu_suspend_set_dbg_restorer(void (*hw_bp_restore)(void *)) | |||
56 | } | 51 | } |
57 | 52 | ||
58 | /** | 53 | /** |
59 | * cpu_suspend | 54 | * cpu_suspend() - function to enter a low-power state |
55 | * @arg: argument to pass to CPU suspend operations | ||
60 | * | 56 | * |
61 | * @arg: argument to pass to the finisher function | 57 | * Return: 0 on success, -EOPNOTSUPP if CPU suspend hook not initialized, CPU |
58 | * operations back-end error code otherwise. | ||
62 | */ | 59 | */ |
63 | int cpu_suspend(unsigned long arg) | 60 | int cpu_suspend(unsigned long arg) |
64 | { | 61 | { |
65 | struct mm_struct *mm = current->active_mm; | 62 | int cpu = smp_processor_id(); |
66 | int ret, cpu = smp_processor_id(); | ||
67 | unsigned long flags; | ||
68 | 63 | ||
69 | /* | 64 | /* |
70 | * If cpu_ops have not been registered or suspend | 65 | * If cpu_ops have not been registered or suspend |
@@ -72,6 +67,21 @@ int cpu_suspend(unsigned long arg) | |||
72 | */ | 67 | */ |
73 | if (!cpu_ops[cpu] || !cpu_ops[cpu]->cpu_suspend) | 68 | if (!cpu_ops[cpu] || !cpu_ops[cpu]->cpu_suspend) |
74 | return -EOPNOTSUPP; | 69 | return -EOPNOTSUPP; |
70 | return cpu_ops[cpu]->cpu_suspend(arg); | ||
71 | } | ||
72 | |||
73 | /* | ||
74 | * __cpu_suspend | ||
75 | * | ||
76 | * arg: argument to pass to the finisher function | ||
77 | * fn: finisher function pointer | ||
78 | * | ||
79 | */ | ||
80 | int __cpu_suspend(unsigned long arg, int (*fn)(unsigned long)) | ||
81 | { | ||
82 | struct mm_struct *mm = current->active_mm; | ||
83 | int ret; | ||
84 | unsigned long flags; | ||
75 | 85 | ||
76 | /* | 86 | /* |
77 | * From this point debug exceptions are disabled to prevent | 87 | * From this point debug exceptions are disabled to prevent |
@@ -86,7 +96,7 @@ int cpu_suspend(unsigned long arg) | |||
86 | * page tables, so that the thread address space is properly | 96 | * page tables, so that the thread address space is properly |
87 | * set-up on function return. | 97 | * set-up on function return. |
88 | */ | 98 | */ |
89 | ret = __cpu_suspend(arg); | 99 | ret = __cpu_suspend_enter(arg, fn); |
90 | if (ret == 0) { | 100 | if (ret == 0) { |
91 | cpu_switch_mm(mm->pgd, mm); | 101 | cpu_switch_mm(mm->pgd, mm); |
92 | flush_tlb_all(); | 102 | flush_tlb_all(); |
@@ -95,7 +105,7 @@ int cpu_suspend(unsigned long arg) | |||
95 | * Restore per-cpu offset before any kernel | 105 | * Restore per-cpu offset before any kernel |
96 | * subsystem relying on it has a chance to run. | 106 | * subsystem relying on it has a chance to run. |
97 | */ | 107 | */ |
98 | set_my_cpu_offset(per_cpu_offset(cpu)); | 108 | set_my_cpu_offset(per_cpu_offset(smp_processor_id())); |
99 | 109 | ||
100 | /* | 110 | /* |
101 | * Restore HW breakpoint registers to sane values | 111 | * Restore HW breakpoint registers to sane values |
diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index 32748c36c477..c5029c1209b4 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig | |||
@@ -25,11 +25,19 @@ config CPU_IDLE_GOV_MENU | |||
25 | bool "Menu governor (for tickless system)" | 25 | bool "Menu governor (for tickless system)" |
26 | default y | 26 | default y |
27 | 27 | ||
28 | config DT_IDLE_STATES | ||
29 | bool | ||
30 | |||
28 | menu "ARM CPU Idle Drivers" | 31 | menu "ARM CPU Idle Drivers" |
29 | depends on ARM | 32 | depends on ARM |
30 | source "drivers/cpuidle/Kconfig.arm" | 33 | source "drivers/cpuidle/Kconfig.arm" |
31 | endmenu | 34 | endmenu |
32 | 35 | ||
36 | menu "ARM64 CPU Idle Drivers" | ||
37 | depends on ARM64 | ||
38 | source "drivers/cpuidle/Kconfig.arm64" | ||
39 | endmenu | ||
40 | |||
33 | menu "MIPS CPU Idle Drivers" | 41 | menu "MIPS CPU Idle Drivers" |
34 | depends on MIPS | 42 | depends on MIPS |
35 | source "drivers/cpuidle/Kconfig.mips" | 43 | source "drivers/cpuidle/Kconfig.mips" |
diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index 38cff69ffe06..e339c7f2c2b7 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm | |||
@@ -7,6 +7,7 @@ config ARM_BIG_LITTLE_CPUIDLE | |||
7 | depends on MCPM | 7 | depends on MCPM |
8 | select ARM_CPU_SUSPEND | 8 | select ARM_CPU_SUSPEND |
9 | select CPU_IDLE_MULTIPLE_DRIVERS | 9 | select CPU_IDLE_MULTIPLE_DRIVERS |
10 | select DT_IDLE_STATES | ||
10 | help | 11 | help |
11 | Select this option to enable CPU idle driver for big.LITTLE based | 12 | Select this option to enable CPU idle driver for big.LITTLE based |
12 | ARM systems. Driver manages CPUs coordination through MCPM and | 13 | ARM systems. Driver manages CPUs coordination through MCPM and |
diff --git a/drivers/cpuidle/Kconfig.arm64 b/drivers/cpuidle/Kconfig.arm64 new file mode 100644 index 000000000000..d0a08ed1b2ee --- /dev/null +++ b/drivers/cpuidle/Kconfig.arm64 | |||
@@ -0,0 +1,14 @@ | |||
1 | # | ||
2 | # ARM64 CPU Idle drivers | ||
3 | # | ||
4 | |||
5 | config ARM64_CPUIDLE | ||
6 | bool "Generic ARM64 CPU idle Driver" | ||
7 | select ARM64_CPU_SUSPEND | ||
8 | select DT_IDLE_STATES | ||
9 | help | ||
10 | Select this to enable generic cpuidle driver for ARM64. | ||
11 | It provides a generic idle driver whose idle states are configured | ||
12 | at run-time through DT nodes. The CPUidle suspend backend is | ||
13 | initialized by calling the CPU operations init idle hook | ||
14 | provided by architecture code. | ||
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 11edb31c55e9..4d177b916f75 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile | |||
@@ -4,6 +4,7 @@ | |||
4 | 4 | ||
5 | obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ | 5 | obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ |
6 | obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o | 6 | obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o |
7 | obj-$(CONFIG_DT_IDLE_STATES) += dt_idle_states.o | ||
7 | 8 | ||
8 | ################################################################################## | 9 | ################################################################################## |
9 | # ARM SoC drivers | 10 | # ARM SoC drivers |
@@ -22,6 +23,10 @@ obj-$(CONFIG_ARM_EXYNOS_CPUIDLE) += cpuidle-exynos.o | |||
22 | obj-$(CONFIG_MIPS_CPS_CPUIDLE) += cpuidle-cps.o | 23 | obj-$(CONFIG_MIPS_CPS_CPUIDLE) += cpuidle-cps.o |
23 | 24 | ||
24 | ############################################################################### | 25 | ############################################################################### |
26 | # ARM64 drivers | ||
27 | obj-$(CONFIG_ARM64_CPUIDLE) += cpuidle-arm64.o | ||
28 | |||
29 | ############################################################################### | ||
25 | # POWERPC drivers | 30 | # POWERPC drivers |
26 | obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o | 31 | obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o |
27 | obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o | 32 | obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o |
diff --git a/drivers/cpuidle/cpuidle-arm64.c b/drivers/cpuidle/cpuidle-arm64.c new file mode 100644 index 000000000000..50997ea942fc --- /dev/null +++ b/drivers/cpuidle/cpuidle-arm64.c | |||
@@ -0,0 +1,133 @@ | |||
1 | /* | ||
2 | * ARM64 generic CPU idle driver. | ||
3 | * | ||
4 | * Copyright (C) 2014 ARM Ltd. | ||
5 | * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #define pr_fmt(fmt) "CPUidle arm64: " fmt | ||
13 | |||
14 | #include <linux/cpuidle.h> | ||
15 | #include <linux/cpumask.h> | ||
16 | #include <linux/cpu_pm.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/of.h> | ||
20 | |||
21 | #include <asm/cpuidle.h> | ||
22 | #include <asm/suspend.h> | ||
23 | |||
24 | #include "dt_idle_states.h" | ||
25 | |||
26 | /* | ||
27 | * arm64_enter_idle_state - Programs CPU to enter the specified state | ||
28 | * | ||
29 | * dev: cpuidle device | ||
30 | * drv: cpuidle driver | ||
31 | * idx: state index | ||
32 | * | ||
33 | * Called from the CPUidle framework to program the device to the | ||
34 | * specified target state selected by the governor. | ||
35 | */ | ||
36 | static int arm64_enter_idle_state(struct cpuidle_device *dev, | ||
37 | struct cpuidle_driver *drv, int idx) | ||
38 | { | ||
39 | int ret; | ||
40 | |||
41 | if (!idx) { | ||
42 | cpu_do_idle(); | ||
43 | return idx; | ||
44 | } | ||
45 | |||
46 | ret = cpu_pm_enter(); | ||
47 | if (!ret) { | ||
48 | /* | ||
49 | * Pass idle state index to cpu_suspend which in turn will | ||
50 | * call the CPU ops suspend protocol with idle index as a | ||
51 | * parameter. | ||
52 | */ | ||
53 | ret = cpu_suspend(idx); | ||
54 | |||
55 | cpu_pm_exit(); | ||
56 | } | ||
57 | |||
58 | return ret ? -1 : idx; | ||
59 | } | ||
60 | |||
61 | static struct cpuidle_driver arm64_idle_driver = { | ||
62 | .name = "arm64_idle", | ||
63 | .owner = THIS_MODULE, | ||
64 | /* | ||
65 | * State at index 0 is standby wfi and considered standard | ||
66 | * on all ARM platforms. If in some platforms simple wfi | ||
67 | * can't be used as "state 0", DT bindings must be implemented | ||
68 | * to work around this issue and allow installing a special | ||
69 | * handler for idle state index 0. | ||
70 | */ | ||
71 | .states[0] = { | ||
72 | .enter = arm64_enter_idle_state, | ||
73 | .exit_latency = 1, | ||
74 | .target_residency = 1, | ||
75 | .power_usage = UINT_MAX, | ||
76 | .flags = CPUIDLE_FLAG_TIME_VALID, | ||
77 | .name = "WFI", | ||
78 | .desc = "ARM64 WFI", | ||
79 | } | ||
80 | }; | ||
81 | |||
82 | static const struct of_device_id arm64_idle_state_match[] __initconst = { | ||
83 | { .compatible = "arm,idle-state", | ||
84 | .data = arm64_enter_idle_state }, | ||
85 | { }, | ||
86 | }; | ||
87 | |||
88 | /* | ||
89 | * arm64_idle_init | ||
90 | * | ||
91 | * Registers the arm64 specific cpuidle driver with the cpuidle | ||
92 | * framework. It relies on core code to parse the idle states | ||
93 | * and initialize them using driver data structures accordingly. | ||
94 | */ | ||
95 | static int __init arm64_idle_init(void) | ||
96 | { | ||
97 | int cpu, ret; | ||
98 | struct cpuidle_driver *drv = &arm64_idle_driver; | ||
99 | |||
100 | /* | ||
101 | * Initialize idle states data, starting at index 1. | ||
102 | * This driver is DT only, if no DT idle states are detected (ret == 0) | ||
103 | * let the driver initialization fail accordingly since there is no | ||
104 | * reason to initialize the idle driver if only wfi is supported. | ||
105 | */ | ||
106 | ret = dt_init_idle_driver(drv, arm64_idle_state_match, 1); | ||
107 | if (ret <= 0) { | ||
108 | if (ret) | ||
109 | pr_err("failed to initialize idle states\n"); | ||
110 | return ret ? : -ENODEV; | ||
111 | } | ||
112 | |||
113 | /* | ||
114 | * Call arch CPU operations in order to initialize | ||
115 | * idle states suspend back-end specific data | ||
116 | */ | ||
117 | for_each_possible_cpu(cpu) { | ||
118 | ret = cpu_init_idle(cpu); | ||
119 | if (ret) { | ||
120 | pr_err("CPU %d failed to init idle CPU ops\n", cpu); | ||
121 | return ret; | ||
122 | } | ||
123 | } | ||
124 | |||
125 | ret = cpuidle_register(drv, NULL); | ||
126 | if (ret) { | ||
127 | pr_err("failed to register cpuidle driver\n"); | ||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | return 0; | ||
132 | } | ||
133 | device_initcall(arm64_idle_init); | ||
diff --git a/drivers/cpuidle/cpuidle-big_little.c b/drivers/cpuidle/cpuidle-big_little.c index ef94c3b81f18..fbc00a1d3c48 100644 --- a/drivers/cpuidle/cpuidle-big_little.c +++ b/drivers/cpuidle/cpuidle-big_little.c | |||
@@ -24,6 +24,8 @@ | |||
24 | #include <asm/smp_plat.h> | 24 | #include <asm/smp_plat.h> |
25 | #include <asm/suspend.h> | 25 | #include <asm/suspend.h> |
26 | 26 | ||
27 | #include "dt_idle_states.h" | ||
28 | |||
27 | static int bl_enter_powerdown(struct cpuidle_device *dev, | 29 | static int bl_enter_powerdown(struct cpuidle_device *dev, |
28 | struct cpuidle_driver *drv, int idx); | 30 | struct cpuidle_driver *drv, int idx); |
29 | 31 | ||
@@ -73,6 +75,12 @@ static struct cpuidle_driver bl_idle_little_driver = { | |||
73 | .state_count = 2, | 75 | .state_count = 2, |
74 | }; | 76 | }; |
75 | 77 | ||
78 | static const struct of_device_id bl_idle_state_match[] __initconst = { | ||
79 | { .compatible = "arm,idle-state", | ||
80 | .data = bl_enter_powerdown }, | ||
81 | { }, | ||
82 | }; | ||
83 | |||
76 | static struct cpuidle_driver bl_idle_big_driver = { | 84 | static struct cpuidle_driver bl_idle_big_driver = { |
77 | .name = "big_idle", | 85 | .name = "big_idle", |
78 | .owner = THIS_MODULE, | 86 | .owner = THIS_MODULE, |
@@ -159,6 +167,7 @@ static int __init bl_idle_driver_init(struct cpuidle_driver *drv, int part_id) | |||
159 | static const struct of_device_id compatible_machine_match[] = { | 167 | static const struct of_device_id compatible_machine_match[] = { |
160 | { .compatible = "arm,vexpress,v2p-ca15_a7" }, | 168 | { .compatible = "arm,vexpress,v2p-ca15_a7" }, |
161 | { .compatible = "samsung,exynos5420" }, | 169 | { .compatible = "samsung,exynos5420" }, |
170 | { .compatible = "samsung,exynos5800" }, | ||
162 | {}, | 171 | {}, |
163 | }; | 172 | }; |
164 | 173 | ||
@@ -190,6 +199,17 @@ static int __init bl_idle_init(void) | |||
190 | if (ret) | 199 | if (ret) |
191 | goto out_uninit_little; | 200 | goto out_uninit_little; |
192 | 201 | ||
202 | /* Start at index 1, index 0 standard WFI */ | ||
203 | ret = dt_init_idle_driver(&bl_idle_big_driver, bl_idle_state_match, 1); | ||
204 | if (ret < 0) | ||
205 | goto out_uninit_big; | ||
206 | |||
207 | /* Start at index 1, index 0 standard WFI */ | ||
208 | ret = dt_init_idle_driver(&bl_idle_little_driver, | ||
209 | bl_idle_state_match, 1); | ||
210 | if (ret < 0) | ||
211 | goto out_uninit_big; | ||
212 | |||
193 | ret = cpuidle_register(&bl_idle_little_driver, NULL); | 213 | ret = cpuidle_register(&bl_idle_little_driver, NULL); |
194 | if (ret) | 214 | if (ret) |
195 | goto out_uninit_big; | 215 | goto out_uninit_big; |
diff --git a/drivers/cpuidle/dt_idle_states.c b/drivers/cpuidle/dt_idle_states.c new file mode 100644 index 000000000000..52f4d11bbf3f --- /dev/null +++ b/drivers/cpuidle/dt_idle_states.c | |||
@@ -0,0 +1,213 @@ | |||
1 | /* | ||
2 | * DT idle states parsing code. | ||
3 | * | ||
4 | * Copyright (C) 2014 ARM Ltd. | ||
5 | * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #define pr_fmt(fmt) "DT idle-states: " fmt | ||
13 | |||
14 | #include <linux/cpuidle.h> | ||
15 | #include <linux/cpumask.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/of.h> | ||
20 | #include <linux/of_device.h> | ||
21 | |||
22 | #include "dt_idle_states.h" | ||
23 | |||
24 | static int init_state_node(struct cpuidle_state *idle_state, | ||
25 | const struct of_device_id *matches, | ||
26 | struct device_node *state_node) | ||
27 | { | ||
28 | int err; | ||
29 | const struct of_device_id *match_id; | ||
30 | |||
31 | match_id = of_match_node(matches, state_node); | ||
32 | if (!match_id) | ||
33 | return -ENODEV; | ||
34 | /* | ||
35 | * CPUidle drivers are expected to initialize the const void *data | ||
36 | * pointer of the passed in struct of_device_id array to the idle | ||
37 | * state enter function. | ||
38 | */ | ||
39 | idle_state->enter = match_id->data; | ||
40 | |||
41 | err = of_property_read_u32(state_node, "wakeup-latency-us", | ||
42 | &idle_state->exit_latency); | ||
43 | if (err) { | ||
44 | u32 entry_latency, exit_latency; | ||
45 | |||
46 | err = of_property_read_u32(state_node, "entry-latency-us", | ||
47 | &entry_latency); | ||
48 | if (err) { | ||
49 | pr_debug(" * %s missing entry-latency-us property\n", | ||
50 | state_node->full_name); | ||
51 | return -EINVAL; | ||
52 | } | ||
53 | |||
54 | err = of_property_read_u32(state_node, "exit-latency-us", | ||
55 | &exit_latency); | ||
56 | if (err) { | ||
57 | pr_debug(" * %s missing exit-latency-us property\n", | ||
58 | state_node->full_name); | ||
59 | return -EINVAL; | ||
60 | } | ||
61 | /* | ||
62 | * If wakeup-latency-us is missing, default to entry+exit | ||
63 | * latencies as defined in idle states bindings | ||
64 | */ | ||
65 | idle_state->exit_latency = entry_latency + exit_latency; | ||
66 | } | ||
67 | |||
68 | err = of_property_read_u32(state_node, "min-residency-us", | ||
69 | &idle_state->target_residency); | ||
70 | if (err) { | ||
71 | pr_debug(" * %s missing min-residency-us property\n", | ||
72 | state_node->full_name); | ||
73 | return -EINVAL; | ||
74 | } | ||
75 | |||
76 | idle_state->flags = CPUIDLE_FLAG_TIME_VALID; | ||
77 | if (of_property_read_bool(state_node, "local-timer-stop")) | ||
78 | idle_state->flags |= CPUIDLE_FLAG_TIMER_STOP; | ||
79 | /* | ||
80 | * TODO: | ||
81 | * replace with kstrdup and pointer assignment when name | ||
82 | * and desc become string pointers | ||
83 | */ | ||
84 | strncpy(idle_state->name, state_node->name, CPUIDLE_NAME_LEN - 1); | ||
85 | strncpy(idle_state->desc, state_node->name, CPUIDLE_DESC_LEN - 1); | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * Check that the idle state is uniform across all CPUs in the CPUidle driver | ||
91 | * cpumask | ||
92 | */ | ||
93 | static bool idle_state_valid(struct device_node *state_node, unsigned int idx, | ||
94 | const cpumask_t *cpumask) | ||
95 | { | ||
96 | int cpu; | ||
97 | struct device_node *cpu_node, *curr_state_node; | ||
98 | bool valid = true; | ||
99 | |||
100 | /* | ||
101 | * Compare idle state phandles for index idx on all CPUs in the | ||
102 | * CPUidle driver cpumask. Start from next logical cpu following | ||
103 | * cpumask_first(cpumask) since that's the CPU state_node was | ||
104 | * retrieved from. If a mismatch is found bail out straight | ||
105 | * away since we certainly hit a firmware misconfiguration. | ||
106 | */ | ||
107 | for (cpu = cpumask_next(cpumask_first(cpumask), cpumask); | ||
108 | cpu < nr_cpu_ids; cpu = cpumask_next(cpu, cpumask)) { | ||
109 | cpu_node = of_cpu_device_node_get(cpu); | ||
110 | curr_state_node = of_parse_phandle(cpu_node, "cpu-idle-states", | ||
111 | idx); | ||
112 | if (state_node != curr_state_node) | ||
113 | valid = false; | ||
114 | |||
115 | of_node_put(curr_state_node); | ||
116 | of_node_put(cpu_node); | ||
117 | if (!valid) | ||
118 | break; | ||
119 | } | ||
120 | |||
121 | return valid; | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * dt_init_idle_driver() - Parse the DT idle states and initialize the | ||
126 | * idle driver states array | ||
127 | * @drv: Pointer to CPU idle driver to be initialized | ||
128 | * @matches: Array of of_device_id match structures to search in for | ||
129 | * compatible idle state nodes. The data pointer for each valid | ||
130 | * struct of_device_id entry in the matches array must point to | ||
131 | * a function with the following signature, that corresponds to | ||
132 | * the CPUidle state enter function signature: | ||
133 | * | ||
134 | * int (*)(struct cpuidle_device *dev, | ||
135 | * struct cpuidle_driver *drv, | ||
136 | * int index); | ||
137 | * | ||
138 | * @start_idx: First idle state index to be initialized | ||
139 | * | ||
140 | * If DT idle states are detected and are valid the state count and states | ||
141 | * array entries in the cpuidle driver are initialized accordingly starting | ||
142 | * from index start_idx. | ||
143 | * | ||
144 | * Return: number of valid DT idle states parsed, <0 on failure | ||
145 | */ | ||
146 | int dt_init_idle_driver(struct cpuidle_driver *drv, | ||
147 | const struct of_device_id *matches, | ||
148 | unsigned int start_idx) | ||
149 | { | ||
150 | struct cpuidle_state *idle_state; | ||
151 | struct device_node *state_node, *cpu_node; | ||
152 | int i, err = 0; | ||
153 | const cpumask_t *cpumask; | ||
154 | unsigned int state_idx = start_idx; | ||
155 | |||
156 | if (state_idx >= CPUIDLE_STATE_MAX) | ||
157 | return -EINVAL; | ||
158 | /* | ||
159 | * We get the idle states for the first logical cpu in the | ||
160 | * driver mask (or cpu_possible_mask if the driver cpumask is not set) | ||
161 | * and we check through idle_state_valid() if they are uniform | ||
162 | * across CPUs, otherwise we hit a firmware misconfiguration. | ||
163 | */ | ||
164 | cpumask = drv->cpumask ? : cpu_possible_mask; | ||
165 | cpu_node = of_cpu_device_node_get(cpumask_first(cpumask)); | ||
166 | |||
167 | for (i = 0; ; i++) { | ||
168 | state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); | ||
169 | if (!state_node) | ||
170 | break; | ||
171 | |||
172 | if (!idle_state_valid(state_node, i, cpumask)) { | ||
173 | pr_warn("%s idle state not valid, bailing out\n", | ||
174 | state_node->full_name); | ||
175 | err = -EINVAL; | ||
176 | break; | ||
177 | } | ||
178 | |||
179 | if (state_idx == CPUIDLE_STATE_MAX) { | ||
180 | pr_warn("State index reached static CPU idle driver states array size\n"); | ||
181 | break; | ||
182 | } | ||
183 | |||
184 | idle_state = &drv->states[state_idx++]; | ||
185 | err = init_state_node(idle_state, matches, state_node); | ||
186 | if (err) { | ||
187 | pr_err("Parsing idle state node %s failed with err %d\n", | ||
188 | state_node->full_name, err); | ||
189 | err = -EINVAL; | ||
190 | break; | ||
191 | } | ||
192 | of_node_put(state_node); | ||
193 | } | ||
194 | |||
195 | of_node_put(state_node); | ||
196 | of_node_put(cpu_node); | ||
197 | if (err) | ||
198 | return err; | ||
199 | /* | ||
200 | * Update the driver state count only if some valid DT idle states | ||
201 | * were detected | ||
202 | */ | ||
203 | if (i) | ||
204 | drv->state_count = state_idx; | ||
205 | |||
206 | /* | ||
207 | * Return the number of present and valid DT idle states, which can | ||
208 | * also be 0 on platforms with missing DT idle states or legacy DT | ||
209 | * configuration predating the DT idle states bindings. | ||
210 | */ | ||
211 | return i; | ||
212 | } | ||
213 | EXPORT_SYMBOL_GPL(dt_init_idle_driver); | ||
diff --git a/drivers/cpuidle/dt_idle_states.h b/drivers/cpuidle/dt_idle_states.h new file mode 100644 index 000000000000..4818134bc65b --- /dev/null +++ b/drivers/cpuidle/dt_idle_states.h | |||
@@ -0,0 +1,7 @@ | |||
1 | #ifndef __DT_IDLE_STATES | ||
2 | #define __DT_IDLE_STATES | ||
3 | |||
4 | int dt_init_idle_driver(struct cpuidle_driver *drv, | ||
5 | const struct of_device_id *matches, | ||
6 | unsigned int start_idx); | ||
7 | #endif | ||
diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c index ca89412f5122..fb9f511cca23 100644 --- a/drivers/cpuidle/governor.c +++ b/drivers/cpuidle/governor.c | |||
@@ -28,7 +28,7 @@ static struct cpuidle_governor * __cpuidle_find_governor(const char *str) | |||
28 | struct cpuidle_governor *gov; | 28 | struct cpuidle_governor *gov; |
29 | 29 | ||
30 | list_for_each_entry(gov, &cpuidle_governors, governor_list) | 30 | list_for_each_entry(gov, &cpuidle_governors, governor_list) |
31 | if (!strnicmp(str, gov->name, CPUIDLE_NAME_LEN)) | 31 | if (!strncasecmp(str, gov->name, CPUIDLE_NAME_LEN)) |
32 | return gov; | 32 | return gov; |
33 | 33 | ||
34 | return NULL; | 34 | return NULL; |