diff options
| author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-09-25 16:18:45 -0400 |
|---|---|---|
| committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-09-25 16:18:45 -0400 |
| commit | eeb1aa5f78caf8d5ec1ef651c09bde34370321d8 (patch) | |
| tree | cc641b7dad3bff3bf1527a99c5d88d1f23d0a563 | |
| parent | 9133664097dd4aca0bed4882a86f0bfccbf07e53 (diff) | |
| parent | d2e5c871ed8a250f7ee1fe34dd52ed5757363fba (diff) | |
Merge branch 'cpuidle/3.18' of https://git.linaro.org/people/daniel.lezcano/linux into pm-cpuidle
Pull ARM cpuidle changes for v3.18 from Daniel Lezcano:
"this pull request contains the following changes:
* Lorenzo Pieralisi implemented a framework to initialize the ARM
cpuidle drivers with the DT. As an example, it provided a couple of
drivers using it: arm64 and big little. The former one is a new
driver while the latter is a change. There was also a patch for
Exynos allowing to use this framework but as it depends on a change
in Samsung's tree, I postponed this patch until the change is visible
after the merge. The set of changes depends on some other changes
made in the ARM64 tree, for this reason a shared branch is used. This
is why there is a merge from arm64 in my pull request. I believe we
already used this procedure.
* Kevin Hilman added the compatible string for the exynos 5800 in the DT"
* 'cpuidle/3.18' of https://git.linaro.org/people/daniel.lezcano/linux:
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
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 |
20 files changed, 1340 insertions, 33 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 | ||
