Skip to contentSkip to conversation

ARM generic idle states

PATCH Superseded Lorenzo Pieralisi 7 participants 22 messages Jul 21, 2014 – Aug 6, 2014

Subsystems (7)

v6 of 9 Superseded Latest is v9 →

lore
Share & tools
lore
#0 lore raw

v6 cover ARM generic idle states

This patch is v6 of a previous posting:

http://lists.infradead.org/pipermail/linux-arm-kernel/2014-June/266488.html

Patchset has been tested on:

- Juno ARM64 platform and Foundation v8 models (based on Trusted Firmware PSCI implementation available at [1])
# Patches to enable idle states on Juno and Foundation models can be made
available, current dts are not in the mainline kernel
- TC2
- Compile tested on Samsung Exynos

Patch for Exynos CPUidle code depends on patch [2].

[1] https://github.com/ARM-software/arm-trusted-firmware
[2] http://lists.infradead.org/pipermail/linux-arm-kernel/2014-July/274179.html

Changes in v6:

- Added ARM64 CPU init idle to CPU operations
- Removed power-rank property; sorting depends on the cpu-idle-states phandle list order now
- Added local-timer-stop flag
- Removed useless TC2 and Exynos DT idle state entry-method bindings
- Removed RTSM dts update
- Updated PSCI suspend parameter and DT bindings
- Updated PSCI parsing code
- Rebased against 3.16-rc6

Changes in v5:

- Added power-rank property to implement state sorting, following a number of on/off list review comments
- Added timer retained bool property
- Ported TC2 big.LITTLE and Exynos drivers to DT initialization
- Renamed s/OF/dt/ throughout the patch
- Incorporated review comments and list discussions in the idle states bindings documents
- Rebased against 3.16-rc2

Changes in v4:

- States sorting using exit-latency
- Added cosmetic review comments
- Dropped RFC
- Rebased against 3.15

Changes in v3:

- Streamlined the idle states bindings and added them to the series
http://www.spinics.net/lists/arm-kernel/msg316299.html
- Sorting states through min-residency+exit-latency
- Added debug strings formatting
- Reworded min-residency-us idle state property
- Removed power-domain properties from idle states waiting for code examples requiring them to be defined

Changes in v2:

- Moved OF parsing code to drivers/cpuidle
- Improved states detection and sorting through linked list
- Split code in generic and ARM64 specific bits
- Moved idle enter function into ARM64 idle driver
- Refactored PSCI idle states register function
- Renamed suspend operations and moved detection to ARM64 idle driver
- Changed the way CPUIDLE_FLAG_TIMER_STOP is handled
- Simplified idle state nodes parsing since according to the latest bindings idle state nodes are a flat list, not hierarchical anymore
- Used min-residency-us to sort the states, to be further discussed

Idle states on most ARM platforms can be characterized by a set of parameters that are platform agnostic and describe the HW idle states features. So far, CPU idle drivers for ARM platforms required the definition of parameters through static tables, duplicating control data for different platforms. Moreover, the lack of standardization on firmware interfaces hampered any standardization effort, resulting in CPU idle drivers for ARM platforms containing duplicated code and platform specific power down routines.

The introduction of the PSCI firmware interface, and more in general, well defined suspend back-ends, allows the definition of generic idle states and the respective kernel infrastructure to support them.

Building on top of DT idle states bindings, that standardize idle states parameters and corresponding suspend back-ends, this patchset provides code that parses DT idle states nodes and builds at run-time the control data infrastructure required by the ARM CPU idle drivers.

Idle states define an entry method (eg PSCI), that requires the respective ARM64 kernel back-end to be invoked to initialize idle states parameters, so that when the idle driver executes the back-end specific entry method a table look-up can be carried out to retrieve the corresponding idle state parameter.

On legacy ARM platforms, the DT idle states are just used to initialize states data.

Lorenzo Pieralisi (7):
Documentation: arm: define DT idle states bindings
drivers: cpuidle: implement DT based idle states infrastructure
arm64: kernel: introduce cpu_init_idle CPU operation
arm64: add PSCI CPU_SUSPEND based cpu_suspend support
drivers: cpuidle: CPU idle ARM64 driver
drivers: cpuidle: initialize big.LITTLE driver through DT
drivers: cpuidle: initialize Exynos driver through DT

 Documentation/devicetree/bindings/arm/cpus.txt     |   8 +
 .../devicetree/bindings/arm/idle-states.txt        | 679 +++++++++++++++++++++
 Documentation/devicetree/bindings/arm/psci.txt     |  14 +-
 arch/arm/boot/dts/exynos4210.dtsi                  |  11 +
 arch/arm/boot/dts/exynos5250.dtsi                  |  11 +
 arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts         |  23 +
 arch/arm64/include/asm/cpu_ops.h                   |   1 +
 arch/arm64/include/asm/cpuidle.h                   |  13 +
 arch/arm64/kernel/Makefile                         |   1 +
 arch/arm64/kernel/cpuidle.c                        |  27 +
 arch/arm64/kernel/psci.c                           |  89 +++
 drivers/cpuidle/Kconfig                            |  13 +
 drivers/cpuidle/Kconfig.arm                        |   2 +
 drivers/cpuidle/Kconfig.arm64                      |  14 +
 drivers/cpuidle/Makefile                           |   5 +
 drivers/cpuidle/cpuidle-arm64.c                    | 128 ++++
 drivers/cpuidle/cpuidle-big_little.c               |  43 +-
 drivers/cpuidle/cpuidle-exynos.c                   |  37 +-
 drivers/cpuidle/dt_idle_states.c                   | 138 +++++
 drivers/cpuidle/dt_idle_states.h                   |   5 +
 20 files changed, 1227 insertions(+), 35 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/idle-states.txt
 create mode 100644 arch/arm64/include/asm/cpuidle.h
 create mode 100644 arch/arm64/kernel/cpuidle.c
 create mode 100644 drivers/cpuidle/Kconfig.arm64
 create mode 100644 drivers/cpuidle/cpuidle-arm64.c
 create mode 100644 drivers/cpuidle/dt_idle_states.c
 create mode 100644 drivers/cpuidle/dt_idle_states.h
Signature · 1 line
1.9.1
#1 lore raw

PATCH v6 1/7 Documentation: arm: define DT idle states bindings

ARM based platforms implement a variety of power management schemes that allow processors to enter idle states at run-time.
The parameters defining these idle states vary on a per-platform basis forcing the OS to hardcode the state parameters in platform specific static tables whose size grows as the number of platforms supported in the kernel increases and hampers device drivers standardization.

Therefore, this patch aims at standardizing idle state device tree bindings for ARM platforms. Bindings define idle state parameters inclusive of entry methods and state latencies, to allow operating systems to retrieve the configuration entries from the device tree and initialize the related power management drivers, paving the way for common code in the kernel to deal with idle states and removing the need for static data in current and previous kernel versions.

ARM64 platforms require the DT to define an entry-method property for idle states.

On system implementing PSCI as an enable-method to enter low-power states the PSCI CPU suspend method requires the power_state parameter to be passed to the PSCI CPU suspend function.

This parameter is specific to a power state and platform specific, therefore must be provided by firmware to the OS in order to enable proper call sequence.

Thus, this patch also adds a property in the PSCI bindings that describes how the PSCI CPU suspend power_state parameter should be defined in DT in all device nodes that rely on PSCI CPU suspend method usage.

---
 Documentation/devicetree/bindings/arm/cpus.txt     |   8 +
 .../devicetree/bindings/arm/idle-states.txt        | 679 +++++++++++++++++++++
 Documentation/devicetree/bindings/arm/psci.txt     |  14 +-
 3 files changed, 700 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/arm/idle-states.txt
Documentation/devicetree/bindings/arm/cpus.txt +8 -0
index 1fe72a0..a44d4fd 100644
@@ -215,6 +215,12 @@ nodes to be present and contain the properties described below.
 		Value type: <phandle>
 		Definition: Specifies the ACC[2] node associated with this CPU.
 
+	- cpu-idle-states
+		Usage: Optional
+		Value type: <prop-encoded-array>
+		Definition:
+			# List of phandles to idle state nodes supported
+			  by this cpu [3].
 
 Example 1 (dual-cluster big.LITTLE system 32-bit):
 
@@ -411,3 +417,5 @@ cpus {
 --
 [1] arm/msm/qcom,saw2.txt
 [2] arm/msm/qcom,kpss-acc.txt
+[3] ARM Linux kernel documentation - idle states bindings
+    Documentation/devicetree/bindings/arm/idle-states.txt
Documentation/devicetree/bindings/arm/idle-states.txt new file +679 -0

679 lines not on this page · raw message

Documentation/devicetree/bindings/arm/psci.txt +13 -1
index b4a58f3..5aa40ed 100644
@@ -50,6 +50,16 @@ Main node optional properties:
 
  - migrate       : Function ID for MIGRATE operation
 
+Device tree nodes that require usage of PSCI CPU_SUSPEND function (ie idle
+state nodes, as per bindings in [1]) must specify the following properties:
+
+- arm,psci-suspend-param
+		Usage: Required for state nodes[1] if the corresponding
+                       idle-states node entry-method property is set
+                       to "psci".
+		Value type: <u32>
+		Definition: power_state parameter to pass to the PSCI
+			    suspend call.
 
 Example:
 
@@ -64,7 +74,6 @@ Case 1: PSCI v0.1 only.
 		migrate		= <0x95c10003>;
 	};
 
-
 Case 2: PSCI v0.2 only
 
 	psci {
@@ -88,3 +97,6 @@ Case 3: PSCI v0.2 and PSCI v0.1.
 
 		...
 	};
+
+[1] Kernel documentation - ARM idle states bindings
+    Documentation/devicetree/bindings/arm/idle-states.txt
Signature · 1 line
1.9.1
#2 lore raw

PATCH v6 1/7 Documentation: arm: define DT idle states bindings

Quoted 26 lines · Lorenzo Pieralisi

ARM based platforms implement a variety of power management schemes that allow processors to enter idle states at run-time.
The parameters defining these idle states vary on a per-platform basis forcing the OS to hardcode the state parameters in platform specific static tables whose size grows as the number of platforms supported in the kernel increases and hampers device drivers standardization.

Therefore, this patch aims at standardizing idle state device tree bindings for ARM platforms. Bindings define idle state parameters inclusive of entry methods and state latencies, to allow operating systems to retrieve the configuration entries from the device tree and initialize the related power management drivers, paving the way for common code in the kernel to deal with idle states and removing the need for static data in current and previous kernel versions.

ARM64 platforms require the DT to define an entry-method property for idle states.

On system implementing PSCI as an enable-method to enter low-power states the PSCI CPU suspend method requires the power_state parameter to be passed to the PSCI CPU suspend function.

This parameter is specific to a power state and platform specific, therefore must be provided by firmware to the OS in order to enable proper call sequence.

Quoted 5 lines

Thus, this patch also adds a property in the PSCI bindings that describes how the PSCI CPU suspend power_state parameter should be defined in DT in all device nodes that rely on PSCI CPU suspend method usage.

Quoted 756 lines
---
 Documentation/devicetree/bindings/arm/cpus.txt     |   8 +
 .../devicetree/bindings/arm/idle-states.txt        | 679 +++++++++++++++++++++
 Documentation/devicetree/bindings/arm/psci.txt     |  14 +-
 3 files changed, 700 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/arm/idle-states.txt
Documentation/devicetree/bindings/arm/cpus.txt +8 -0
index 1fe72a0..a44d4fd 100644
@@ -215,6 +215,12 @@ nodes to be present and contain the properties described below.
 		Value type: <phandle>
 		Definition: Specifies the ACC[2] node associated with this CPU.
 
+	- cpu-idle-states
+		Usage: Optional
+		Value type: <prop-encoded-array>
+		Definition:
+			# List of phandles to idle state nodes supported
+			  by this cpu [3].
 
 Example 1 (dual-cluster big.LITTLE system 32-bit):
 
@@ -411,3 +417,5 @@ cpus {
 --
 [1] arm/msm/qcom,saw2.txt
 [2] arm/msm/qcom,kpss-acc.txt
+[3] ARM Linux kernel documentation - idle states bindings
+    Documentation/devicetree/bindings/arm/idle-states.txt
Documentation/devicetree/bindings/arm/idle-states.txt new file +679 -0
new file mode 100644
index 0000000..37375c7
@@ -0,0 +1,679 @@
+==========================================
+ARM idle states binding description
+==========================================
+
+==========================================
+1 - Introduction
+==========================================
+
+ARM systems contain HW capable of managing power consumption dynamically,
+where cores can be put in different low-power states (ranging from simple
+wfi to power gating) according to OS PM policies. The CPU states representing
+the range of dynamic idle states that a processor can enter at run-time, can be
+specified through device tree bindings representing the parameters required
+to enter/exit specific idle states on a given processor.
+
+According to the Server Base System Architecture document (SBSA, [3]), the
+power states an ARM CPU can be put into are identified by the following list:
+
+- Running
+- Idle_standby
+- Idle_retention
+- Sleep
+- Off
+
+The power states described in the SBSA document define the basic CPU states on
+top of which ARM platforms implement power management schemes that allow an OS
+PM implementation to put the processor in different idle states (which include
+states listed above; "off" state is not an idle state since it does not have
+wake-up capabilities, hence it is not considered in this document).
+
+Idle state parameters (eg entry latency) are platform specific and need to be
+characterized with bindings that provide the required information to OS PM
+code so that it can build the required tables and use them at runtime.
+
+The device tree binding definition for ARM idle states is the subject of this
+document.
+
+===========================================
+2 - idle-states definitions
+===========================================
+
+Idle states are characterized for a specific system through a set of
+timing and energy related properties, that underline the HW behaviour
+triggered upon idle states entry and exit.
+
+The following diagram depicts the CPU execution phases and related timing
+properties required to enter and exit an idle state:
+
+..__[EXEC]__|__[PREP]__|__[ENTRY]__|__[IDLE]__|__[EXIT]__|__[EXEC]__..
+	    |          |           |          |          |
+
+	    |<------ entry ------->|
+	    |       latency        |
+					      |<- exit ->|
+					      |  latency |
+	    |<-------- min-residency -------->|
+		       |<-------  wakeup-latency ------->|
+
+		Diagram 1: CPU idle state execution phases
+
+EXEC:	Normal CPU execution.
+
+PREP:	Preparation phase before committing the hardware to idle mode
+	like cache flushing. This is abortable on pending wake-up
+	event conditions. The abort latency is assumed to be negligible
+	(i.e. less than the ENTRY + EXIT duration). If aborted, CPU
+	goes back to EXEC. This phase is optional. If not abortable,
+	this should be included in the ENTRY phase instead.
+
+ENTRY:	The hardware is committed to idle mode. This period must run
+	to completion up to IDLE before anything else can happen.
+
+IDLE:	This is the actual energy-saving idle period. This may last
+	between 0 and infinite time, until a wake-up event occurs.
+
+EXIT:	Period during which the CPU is brought back to operational
+	mode (EXEC).
+
+entry-latency: Worst case latency required to enter the idle state. The
+exit-latency may be guaranteed only after entry-latency has passed.
+
+min-residency: Minimum period, including preparation and entry, for a given
+idle state to be worthwhile energywise.
+
+wakeup-latency: Maximum delay between the signaling of a wake-up event and the
+CPU being able to execute normal code again. If not specified, this is assumed
+to be entry-latency + exit-latency.
+
+These timing parameters can be used by an OS in different circumstances.
+
+An idle CPU requires the expected min-residency time to select the most
+appropriate idle state based on the expected expiry time of the next IRQ
+(ie wake-up) that causes the CPU to return to the EXEC phase.
+
+An operating system scheduler may need to compute the shortest wake-up delay
+for CPUs in the system by detecting how long will it take to get a CPU out
+of an idle state, eg:
+
+wakeup-delay = exit-latency + max(entry-latency - (now - entry-timestamp), 0)
+
+In other words, the scheduler can make its scheduling decision by selecting
+(eg waking-up) the CPU with the shortest wake-up latency.
+The wake-up latency must take into account the entry latency if that period
+has not expired. The abortable nature of the PREP period can be ignored
+if it cannot be relied upon (e.g. the PREP deadline may occur much sooner than
+the worst case since it depends on the CPU operating conditions, ie caches
+state).
+
+An OS has to reliably probe the wakeup-latency since some devices can enforce
+latency constraints guarantees to work properly, so the OS has to detect the
+worst case wake-up latency it can incur if a CPU is allowed to enter an
+idle state, and possibly to prevent that to guarantee reliable device
+functioning.
+
+The min-residency time parameter deserves further explanation since it is
+expressed in time units but must factor in energy consumption coefficients.
+
+The energy consumption of a cpu when it enters a power state can be roughly
+characterised by the following graph:
+
+               |
+               |
+               |
+           e   |
+           n   |                                      /---
+           e   |                               /------
+           r   |                        /------
+           g   |                  /-----
+           y   |           /------
+               |       ----
+               |      /|
+               |     / |
+               |    /  |
+               |   /   |
+               |  /    |
+               | /     |
+               |/      |
+          -----|-------+----------------------------------
+              0|       1                              time(ms)
+
+		Graph 1: Energy vs time example
+
+The graph is split in two parts delimited by time 1ms on the X-axis.
+The graph curve with X-axis values = { x | 0 < x < 1ms } has a steep slope
+and denotes the energy costs incurred whilst entering and leaving the idle
+state.
+The graph curve in the area delimited by X-axis values = {x | x > 1ms } has
+shallower slope and essentially represents the energy consumption of the idle
+state.
+
+min-residency is defined for a given idle state as the minimum expected
+residency time for a state (inclusive of preparation and entry) after
+which choosing that state become the most energy efficient option. A good
+way to visualise this, is by taking the same graph above and comparing some
+states energy consumptions plots.
+
+For sake of simplicity, let's consider a system with two idle states IDLE1,
+and IDLE2:
+
+          |
+          |
+          |
+          |                                                  /-- IDLE1
+       e  |                                              /---
+       n  |                                         /----
+       e  |                                     /---
+       r  |                                /-----/--------- IDLE2
+       g  |                    /-------/---------
+       y  |        ------------    /---|
+          |       /           /----    |
+          |      /        /---         |
+          |     /    /----             |
+          |    / /---                  |
+          |   ---                      |
+          |  /                         |
+          | /                          |
+          |/                           |                  time
+       ---/----------------------------+------------------------
+          |IDLE1-energy < IDLE2-energy | IDLE2-energy < IDLE1-energy
+                                       |
+                                IDLE2-min-residency
+
+		Graph 2: idle states min-residency example
+
+In graph 2 above, that takes into account idle states entry/exit energy
+costs, it is clear that if the idle state residency time (ie time till next
+wake-up IRQ) is less than IDLE2-min-residency, IDLE1 is the better idle state
+choice energywise.
+
+This is mainly down to the fact that IDLE1 entry/exit energy costs are lower
+than IDLE2.
+
+However, the lower power consumption (ie shallower energy curve slope) of idle
+state IDLE2 implies that after a suitable time, IDLE2 becomes more energy
+efficient.
+
+The time at which IDLE2 becomes more energy efficient than IDLE1 (and other
+shallower states in a system with multiple idle states) is defined
+IDLE2-min-residency and corresponds to the time when energy consumption of
+IDLE1 and IDLE2 states breaks even.
+
+The definitions provided in this section underpin the idle states
+properties specification that is the subject of the following sections.
+
+===========================================
+3 - idle-states node
+===========================================
+
+ARM processor idle states are defined within the idle-states node, which is
+a direct child of the cpus node [1] and provides a container where the
+processor idle states, defined as device tree nodes, are listed.
+
+- idle-states node
+
+	Usage: Optional - On ARM systems, it is a container of processor idle
+			  states nodes. If the system does not provide CPU
+			  power management capabilities or the processor just
+			  supports idle_standby an idle-states node is not
+			  required.
+
+	Description: idle-states node is a container node, where its
+		     subnodes describe the CPU idle states.
+
+	Node name must be "idle-states".
+
+	The idle-states node's parent node must be the cpus node.
+
+	The idle-states node's child nodes can be:
+
+	- one or more state nodes
+
+	Any other configuration is considered invalid.
+
+	An idle-states node defines the following properties:
+
+	- entry-method
+		Value type: <stringlist>
+		Usage and definition depend on ARM architecture version.
+			# On ARM v8 64-bit this property is required and must
+			  be one of:
+			   - "psci" (see bindings in [2])
+			# On ARM 32-bit systems this property is optional
+
+The nodes describing the idle states (state) can only be defined within the
+idle-states node, any other configuration is considered invalid and therefore
+must be ignored.
+
+===========================================
+4 - state node
+===========================================
+
+A state node represents an idle state description and must be defined as
+follows:
+
+- state node
+
+	Description: must be child of the idle-states node
+
+	The state node name shall follow standard device tree naming
+	rules ([5], 2.2.1 "Node names"), in particular state nodes which
+	are siblings within a single common parent must be given a unique name.
+
+	The idle state entered by executing the wfi instruction (idle_standby
+	SBSA,[3][4]) is considered standard on all ARM platforms and therefore
+	must not be listed.
+
+	With the definitions provided above, the following list represents
+	the valid properties for a state node:
+
+	- compatible
+		Usage: Required
+		Value type: <stringlist>
+		Definition: Must be "arm,idle-state".
+
+	- local-timer-stop
+		Usage: See definition
+		Value type: <none>
+		Definition: if present the CPU local timer control logic is
+			    lost on state entry, otherwise it is retained.
+
+	- entry-latency-us
+		Usage: Required
+		Value type: <prop-encoded-array>
+		Definition: u32 value representing worst case latency in
+			    microseconds required to enter the idle state.
+			    The exit-latency-us duration may be guaranteed
+			    only after entry-latency-us has passed.
+
+	- exit-latency-us
+		Usage: Required
+		Value type: <prop-encoded-array>
+		Definition: u32 value representing worst case latency
+			    in microseconds required to exit the idle state.
+
+	- min-residency-us
+		Usage: Required
+		Value type: <prop-encoded-array>
+		Definition: u32 value representing minimum residency duration
+			    in microseconds, inclusive of preparation and
+			    entry, for this idle state to be considered
+			    worthwhile energy wise (refer to section 2 of
+			    this document for a complete description).
+
+	- wakeup-latency-us:
+		Usage: Optional
+		Value type: <prop-encoded-array>
+		Definition: u32 value representing maximum delay between the
+			    signaling of a wake-up event and the CPU being
+			    able to execute normal code again. If omitted,
+			    this is assumed to be equal to:
+
+				entry-latency-us + exit-latency-us
+
+			    It is important to supply this value on systems
+			    where the duration of PREP phase (see diagram 1,
+			    section 2) is non-neglibigle.
+			    In such systems entry-latency-us + exit-latency-us
+			    will exceed wakeup-latency-us by this duration.
+
+	In addition to the properties listed above, a state node may require
+	additional properties specifics to the entry-method defined in the
+	idle-states node, please refer to the entry-method bindings
+	documentation for properties definitions.
+
+===========================================
+4 - Examples
+===========================================
+
+Example 1 (ARM 64-bit, 16-cpu system, PSCI enable-method):
+
+cpus {
+	#size-cells = <0>;
+	#address-cells = <2>;
+
+	CPU0: cpu at 0 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a57";
+		reg = <0x0 0x0>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+				   &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU1: cpu at 1 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a57";
+		reg = <0x0 0x1>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+				   &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU2: cpu at 100 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a57";
+		reg = <0x0 0x100>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+				   &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU3: cpu at 101 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a57";
+		reg = <0x0 0x101>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+				   &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU4: cpu at 10000 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a57";
+		reg = <0x0 0x10000>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+				   &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU5: cpu at 10001 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a57";
+		reg = <0x0 0x10001>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+				   &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU6: cpu at 10100 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a57";
+		reg = <0x0 0x10100>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+				   &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU7: cpu at 10101 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a57";
+		reg = <0x0 0x10101>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+				   &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU8: cpu at 100000000 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a53";
+		reg = <0x1 0x0>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+				   &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU9: cpu at 100000001 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a53";
+		reg = <0x1 0x1>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+				   &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU10: cpu at 100000100 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a53";
+		reg = <0x1 0x100>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+				   &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU11: cpu at 100000101 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a53";
+		reg = <0x1 0x101>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+				   &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU12: cpu at 100010000 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a53";
+		reg = <0x1 0x10000>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+				   &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU13: cpu at 100010001 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a53";
+		reg = <0x1 0x10001>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+				   &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU14: cpu at 100010100 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a53";
+		reg = <0x1 0x10100>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+				   &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU15: cpu at 100010101 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a53";
+		reg = <0x1 0x10101>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+				   &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+	};
+
+	idle-states {
+		entry-method = "arm,psci";
+
+		CPU_RETENTION_0_0: cpu-retention-0-0 {
+			compatible = "arm,idle-state";
+			arm,psci-suspend-param = <0x0010000>;
+			entry-latency-us = <20>;
+			exit-latency-us = <40>;
+			min-residency-us = <80>;
+		};
+
+		CLUSTER_RETENTION_0: cluster-retention-0 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			arm,psci-suspend-param = <0x1010000>;
+			entry-latency-us = <50>;
+			exit-latency-us = <100>;
+			min-residency-us = <250>;
+			wakeup-latency-us = <130>;
+		};
+
+		CPU_SLEEP_0_0: cpu-sleep-0-0 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			arm,psci-suspend-param = <0x0010000>;
+			entry-latency-us = <250>;
+			exit-latency-us = <500>;
+			min-residency-us = <950>;
+		};
+
+		CLUSTER_SLEEP_0: cluster-sleep-0 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			arm,psci-suspend-param = <0x1010000>;
+			entry-latency-us = <600>;
+			exit-latency-us = <1100>;
+			min-residency-us = <2700>;
+			wakeup-latency-us = <1500>;
+		};
+
+		CPU_RETENTION_1_0: cpu-retention-1-0 {
+			compatible = "arm,idle-state";
+			arm,psci-suspend-param = <0x0010000>;
+			entry-latency-us = <20>;
+			exit-latency-us = <40>;
+			min-residency-us = <90>;
+		};
+
+		CLUSTER_RETENTION_1: cluster-retention-1 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			arm,psci-suspend-param = <0x1010000>;
+			entry-latency-us = <50>;
+			exit-latency-us = <100>;
+			min-residency-us = <270>;
+			wakeup-latency-us = <100>;
+		};
+
+		CPU_SLEEP_1_0: cpu-sleep-1-0 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			arm,psci-suspend-param = <0x0010000>;
+			entry-latency-us = <70>;
+			exit-latency-us = <100>;
+			min-residency-us = <300>;
+			wakeup-latency-us = <150>;
+		};
+
+		CLUSTER_SLEEP_1: cluster-sleep-1 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			arm,psci-suspend-param = <0x1010000>;
+			entry-latency-us = <500>;
+			exit-latency-us = <1200>;
+			min-residency-us = <3500>;
+			wakeup-latency-us = <1300>;
+		};
+	};
+
+};
+
+Example 2 (ARM 32-bit, 8-cpu system, two clusters):
+
+cpus {
+	#size-cells = <0>;
+	#address-cells = <1>;
+
+	CPU0: cpu at 0 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a15";
+		reg = <0x0>;
+		cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU1: cpu at 1 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a15";
+		reg = <0x1>;
+		cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU2: cpu at 2 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a15";
+		reg = <0x2>;
+		cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU3: cpu at 3 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a15";
+		reg = <0x3>;
+		cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU4: cpu at 100 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a7";
+		reg = <0x100>;
+		cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU5: cpu at 101 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a7";
+		reg = <0x101>;
+		cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU6: cpu at 102 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a7";
+		reg = <0x102>;
+		cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU7: cpu at 103 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a7";
+		reg = <0x103>;
+		cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+	};
+
+	idle-states {
+		CPU_SLEEP_0_0: cpu-sleep-0-0 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			entry-latency-us = <200>;
+			exit-latency-us = <100>;
+			min-residency-us = <400>;
+			wakeup-latency-us = <250>;
+		};
+
+		CLUSTER_SLEEP_0: cluster-sleep-0 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			entry-latency-us = <500>;
+			exit-latency-us = <1500>;
+			min-residency-us = <2500>;
+			wakeup-latency-us = <1700>;
+		};
+
+		CPU_SLEEP_1_0: cpu-sleep-1-0 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			entry-latency-us = <300>;
+			exit-latency-us = <500>;
+			min-residency-us = <900>;
+			wakeup-latency-us = <600>;
+		};
+
+		CLUSTER_SLEEP_1: cluster-sleep-1 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			entry-latency-us = <800>;
+			exit-latency-us = <2000>;
+			min-residency-us = <6500>;
+			wakeup-latency-us = <2300>;
+		};
+	};
+
+};
+
+===========================================
+5 - References
+===========================================
+
+[1] ARM Linux Kernel documentation - CPUs bindings
+    Documentation/devicetree/bindings/arm/cpus.txt
+
+[2] ARM Linux Kernel documentation - PSCI bindings
+    Documentation/devicetree/bindings/arm/psci.txt
+
+[3] ARM Server Base System Architecture (SBSA)
+    http://infocenter.arm.com/help/index.jsp
+
+[4] ARM Architecture Reference Manuals
+    http://infocenter.arm.com/help/index.jsp
+
+[5] ePAPR standard
+    https://www.power.org/documentation/epapr-version-1-1/
Documentation/devicetree/bindings/arm/psci.txt +13 -1
index b4a58f3..5aa40ed 100644
@@ -50,6 +50,16 @@ Main node optional properties:
 
  - migrate       : Function ID for MIGRATE operation
 
+Device tree nodes that require usage of PSCI CPU_SUSPEND function (ie idle
+state nodes, as per bindings in [1]) must specify the following properties:
+
+- arm,psci-suspend-param
+		Usage: Required for state nodes[1] if the corresponding
+                       idle-states node entry-method property is set
+                       to "psci".
+		Value type: <u32>
+		Definition: power_state parameter to pass to the PSCI
+			    suspend call.
 
 Example:
 
@@ -64,7 +74,6 @@ Case 1: PSCI v0.1 only.
 		migrate		= <0x95c10003>;
 	};
 
-
 Case 2: PSCI v0.2 only
 
 	psci {
@@ -88,3 +97,6 @@ Case 3: PSCI v0.2 and PSCI v0.1.
 
...
 	};
+
+[1] Kernel documentation - ARM idle states bindings
+    Documentation/devicetree/bindings/arm/idle-states.txt
Signature · 1 line
1.9.1
#3 lore raw

PATCH v6 1/7 Documentation: arm: define DT idle states bindings

In reply to Lorenzo Pieralisi
Quoted 26 lines · Lorenzo Pieralisi

ARM based platforms implement a variety of power management schemes that allow processors to enter idle states at run-time.
The parameters defining these idle states vary on a per-platform basis forcing the OS to hardcode the state parameters in platform specific static tables whose size grows as the number of platforms supported in the kernel increases and hampers device drivers standardization.

Therefore, this patch aims at standardizing idle state device tree bindings for ARM platforms. Bindings define idle state parameters inclusive of entry methods and state latencies, to allow operating systems to retrieve the configuration entries from the device tree and initialize the related power management drivers, paving the way for common code in the kernel to deal with idle states and removing the need for static data in current and previous kernel versions.

ARM64 platforms require the DT to define an entry-method property for idle states.

On system implementing PSCI as an enable-method to enter low-power states the PSCI CPU suspend method requires the power_state parameter to be passed to the PSCI CPU suspend function.

This parameter is specific to a power state and platform specific, therefore must be provided by firmware to the OS in order to enable proper call sequence.

Quoted 5 lines

Thus, this patch also adds a property in the PSCI bindings that describes how the PSCI CPU suspend power_state parameter should be defined in DT in all device nodes that rely on PSCI CPU suspend method usage.

Rob

Quoted 755 lines
---
 Documentation/devicetree/bindings/arm/cpus.txt     |   8 +
 .../devicetree/bindings/arm/idle-states.txt        | 679 +++++++++++++++++++++
 Documentation/devicetree/bindings/arm/psci.txt     |  14 +-
 3 files changed, 700 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/arm/idle-states.txt
Documentation/devicetree/bindings/arm/cpus.txt +8 -0
index 1fe72a0..a44d4fd 100644
@@ -215,6 +215,12 @@ nodes to be present and contain the properties described below.
                Value type: <phandle>
                Definition: Specifies the ACC[2] node associated with this CPU.
 
+       - cpu-idle-states
+               Usage: Optional
+               Value type: <prop-encoded-array>
+               Definition:
+                       # List of phandles to idle state nodes supported
+                         by this cpu [3].
 
 Example 1 (dual-cluster big.LITTLE system 32-bit):
 
@@ -411,3 +417,5 @@ cpus {
 --
 [1] arm/msm/qcom,saw2.txt
 [2] arm/msm/qcom,kpss-acc.txt
+[3] ARM Linux kernel documentation - idle states bindings
+    Documentation/devicetree/bindings/arm/idle-states.txt
Documentation/devicetree/bindings/arm/idle-states.txt new file +679 -0
new file mode 100644
index 0000000..37375c7
@@ -0,0 +1,679 @@
+==========================================
+ARM idle states binding description
+==========================================
+
+==========================================
+1 - Introduction
+==========================================
+
+ARM systems contain HW capable of managing power consumption dynamically,
+where cores can be put in different low-power states (ranging from simple
+wfi to power gating) according to OS PM policies. The CPU states representing
+the range of dynamic idle states that a processor can enter at run-time, can be
+specified through device tree bindings representing the parameters required
+to enter/exit specific idle states on a given processor.
+
+According to the Server Base System Architecture document (SBSA, [3]), the
+power states an ARM CPU can be put into are identified by the following list:
+
+- Running
+- Idle_standby
+- Idle_retention
+- Sleep
+- Off
+
+The power states described in the SBSA document define the basic CPU states on
+top of which ARM platforms implement power management schemes that allow an OS
+PM implementation to put the processor in different idle states (which include
+states listed above; "off" state is not an idle state since it does not have
+wake-up capabilities, hence it is not considered in this document).
+
+Idle state parameters (eg entry latency) are platform specific and need to be
+characterized with bindings that provide the required information to OS PM
+code so that it can build the required tables and use them at runtime.
+
+The device tree binding definition for ARM idle states is the subject of this
+document.
+
+===========================================
+2 - idle-states definitions
+===========================================
+
+Idle states are characterized for a specific system through a set of
+timing and energy related properties, that underline the HW behaviour
+triggered upon idle states entry and exit.
+
+The following diagram depicts the CPU execution phases and related timing
+properties required to enter and exit an idle state:
+
+..__[EXEC]__|__[PREP]__|__[ENTRY]__|__[IDLE]__|__[EXIT]__|__[EXEC]__..
+           |          |           |          |          |
+
+           |<------ entry ------->|
+           |       latency        |
+                                             |<- exit ->|
+                                             |  latency |
+           |<-------- min-residency -------->|
+                      |<-------  wakeup-latency ------->|
+
+               Diagram 1: CPU idle state execution phases
+
+EXEC:  Normal CPU execution.
+
+PREP:  Preparation phase before committing the hardware to idle mode
+       like cache flushing. This is abortable on pending wake-up
+       event conditions. The abort latency is assumed to be negligible
+       (i.e. less than the ENTRY + EXIT duration). If aborted, CPU
+       goes back to EXEC. This phase is optional. If not abortable,
+       this should be included in the ENTRY phase instead.
+
+ENTRY: The hardware is committed to idle mode. This period must run
+       to completion up to IDLE before anything else can happen.
+
+IDLE:  This is the actual energy-saving idle period. This may last
+       between 0 and infinite time, until a wake-up event occurs.
+
+EXIT:  Period during which the CPU is brought back to operational
+       mode (EXEC).
+
+entry-latency: Worst case latency required to enter the idle state. The
+exit-latency may be guaranteed only after entry-latency has passed.
+
+min-residency: Minimum period, including preparation and entry, for a given
+idle state to be worthwhile energywise.
+
+wakeup-latency: Maximum delay between the signaling of a wake-up event and the
+CPU being able to execute normal code again. If not specified, this is assumed
+to be entry-latency + exit-latency.
+
+These timing parameters can be used by an OS in different circumstances.
+
+An idle CPU requires the expected min-residency time to select the most
+appropriate idle state based on the expected expiry time of the next IRQ
+(ie wake-up) that causes the CPU to return to the EXEC phase.
+
+An operating system scheduler may need to compute the shortest wake-up delay
+for CPUs in the system by detecting how long will it take to get a CPU out
+of an idle state, eg:
+
+wakeup-delay = exit-latency + max(entry-latency - (now - entry-timestamp), 0)
+
+In other words, the scheduler can make its scheduling decision by selecting
+(eg waking-up) the CPU with the shortest wake-up latency.
+The wake-up latency must take into account the entry latency if that period
+has not expired. The abortable nature of the PREP period can be ignored
+if it cannot be relied upon (e.g. the PREP deadline may occur much sooner than
+the worst case since it depends on the CPU operating conditions, ie caches
+state).
+
+An OS has to reliably probe the wakeup-latency since some devices can enforce
+latency constraints guarantees to work properly, so the OS has to detect the
+worst case wake-up latency it can incur if a CPU is allowed to enter an
+idle state, and possibly to prevent that to guarantee reliable device
+functioning.
+
+The min-residency time parameter deserves further explanation since it is
+expressed in time units but must factor in energy consumption coefficients.
+
+The energy consumption of a cpu when it enters a power state can be roughly
+characterised by the following graph:
+
+               |
+               |
+               |
+           e   |
+           n   |                                      /---
+           e   |                               /------
+           r   |                        /------
+           g   |                  /-----
+           y   |           /------
+               |       ----
+               |      /|
+               |     / |
+               |    /  |
+               |   /   |
+               |  /    |
+               | /     |
+               |/      |
+          -----|-------+----------------------------------
+              0|       1                              time(ms)
+
+               Graph 1: Energy vs time example
+
+The graph is split in two parts delimited by time 1ms on the X-axis.
+The graph curve with X-axis values = { x | 0 < x < 1ms } has a steep slope
+and denotes the energy costs incurred whilst entering and leaving the idle
+state.
+The graph curve in the area delimited by X-axis values = {x | x > 1ms } has
+shallower slope and essentially represents the energy consumption of the idle
+state.
+
+min-residency is defined for a given idle state as the minimum expected
+residency time for a state (inclusive of preparation and entry) after
+which choosing that state become the most energy efficient option. A good
+way to visualise this, is by taking the same graph above and comparing some
+states energy consumptions plots.
+
+For sake of simplicity, let's consider a system with two idle states IDLE1,
+and IDLE2:
+
+          |
+          |
+          |
+          |                                                  /-- IDLE1
+       e  |                                              /---
+       n  |                                         /----
+       e  |                                     /---
+       r  |                                /-----/--------- IDLE2
+       g  |                    /-------/---------
+       y  |        ------------    /---|
+          |       /           /----    |
+          |      /        /---         |
+          |     /    /----             |
+          |    / /---                  |
+          |   ---                      |
+          |  /                         |
+          | /                          |
+          |/                           |                  time
+       ---/----------------------------+------------------------
+          |IDLE1-energy < IDLE2-energy | IDLE2-energy < IDLE1-energy
+                                       |
+                                IDLE2-min-residency
+
+               Graph 2: idle states min-residency example
+
+In graph 2 above, that takes into account idle states entry/exit energy
+costs, it is clear that if the idle state residency time (ie time till next
+wake-up IRQ) is less than IDLE2-min-residency, IDLE1 is the better idle state
+choice energywise.
+
+This is mainly down to the fact that IDLE1 entry/exit energy costs are lower
+than IDLE2.
+
+However, the lower power consumption (ie shallower energy curve slope) of idle
+state IDLE2 implies that after a suitable time, IDLE2 becomes more energy
+efficient.
+
+The time at which IDLE2 becomes more energy efficient than IDLE1 (and other
+shallower states in a system with multiple idle states) is defined
+IDLE2-min-residency and corresponds to the time when energy consumption of
+IDLE1 and IDLE2 states breaks even.
+
+The definitions provided in this section underpin the idle states
+properties specification that is the subject of the following sections.
+
+===========================================
+3 - idle-states node
+===========================================
+
+ARM processor idle states are defined within the idle-states node, which is
+a direct child of the cpus node [1] and provides a container where the
+processor idle states, defined as device tree nodes, are listed.
+
+- idle-states node
+
+       Usage: Optional - On ARM systems, it is a container of processor idle
+                         states nodes. If the system does not provide CPU
+                         power management capabilities or the processor just
+                         supports idle_standby an idle-states node is not
+                         required.
+
+       Description: idle-states node is a container node, where its
+                    subnodes describe the CPU idle states.
+
+       Node name must be "idle-states".
+
+       The idle-states node's parent node must be the cpus node.
+
+       The idle-states node's child nodes can be:
+
+       - one or more state nodes
+
+       Any other configuration is considered invalid.
+
+       An idle-states node defines the following properties:
+
+       - entry-method
+               Value type: <stringlist>
+               Usage and definition depend on ARM architecture version.
+                       # On ARM v8 64-bit this property is required and must
+                         be one of:
+                          - "psci" (see bindings in [2])
+                       # On ARM 32-bit systems this property is optional
+
+The nodes describing the idle states (state) can only be defined within the
+idle-states node, any other configuration is considered invalid and therefore
+must be ignored.
+
+===========================================
+4 - state node
+===========================================
+
+A state node represents an idle state description and must be defined as
+follows:
+
+- state node
+
+       Description: must be child of the idle-states node
+
+       The state node name shall follow standard device tree naming
+       rules ([5], 2.2.1 "Node names"), in particular state nodes which
+       are siblings within a single common parent must be given a unique name.
+
+       The idle state entered by executing the wfi instruction (idle_standby
+       SBSA,[3][4]) is considered standard on all ARM platforms and therefore
+       must not be listed.
+
+       With the definitions provided above, the following list represents
+       the valid properties for a state node:
+
+       - compatible
+               Usage: Required
+               Value type: <stringlist>
+               Definition: Must be "arm,idle-state".
+
+       - local-timer-stop
+               Usage: See definition
+               Value type: <none>
+               Definition: if present the CPU local timer control logic is
+                           lost on state entry, otherwise it is retained.
+
+       - entry-latency-us
+               Usage: Required
+               Value type: <prop-encoded-array>
+               Definition: u32 value representing worst case latency in
+                           microseconds required to enter the idle state.
+                           The exit-latency-us duration may be guaranteed
+                           only after entry-latency-us has passed.
+
+       - exit-latency-us
+               Usage: Required
+               Value type: <prop-encoded-array>
+               Definition: u32 value representing worst case latency
+                           in microseconds required to exit the idle state.
+
+       - min-residency-us
+               Usage: Required
+               Value type: <prop-encoded-array>
+               Definition: u32 value representing minimum residency duration
+                           in microseconds, inclusive of preparation and
+                           entry, for this idle state to be considered
+                           worthwhile energy wise (refer to section 2 of
+                           this document for a complete description).
+
+       - wakeup-latency-us:
+               Usage: Optional
+               Value type: <prop-encoded-array>
+               Definition: u32 value representing maximum delay between the
+                           signaling of a wake-up event and the CPU being
+                           able to execute normal code again. If omitted,
+                           this is assumed to be equal to:
+
+                               entry-latency-us + exit-latency-us
+
+                           It is important to supply this value on systems
+                           where the duration of PREP phase (see diagram 1,
+                           section 2) is non-neglibigle.
+                           In such systems entry-latency-us + exit-latency-us
+                           will exceed wakeup-latency-us by this duration.
+
+       In addition to the properties listed above, a state node may require
+       additional properties specifics to the entry-method defined in the
+       idle-states node, please refer to the entry-method bindings
+       documentation for properties definitions.
+
+===========================================
+4 - Examples
+===========================================
+
+Example 1 (ARM 64-bit, 16-cpu system, PSCI enable-method):
+
+cpus {
+       #size-cells = <0>;
+       #address-cells = <2>;
+
+       CPU0: cpu at 0 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a57";
+               reg = <0x0 0x0>;
+               enable-method = "psci";
+               cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+                                  &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+       };
+
+       CPU1: cpu at 1 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a57";
+               reg = <0x0 0x1>;
+               enable-method = "psci";
+               cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+                                  &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+       };
+
+       CPU2: cpu at 100 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a57";
+               reg = <0x0 0x100>;
+               enable-method = "psci";
+               cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+                                  &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+       };
+
+       CPU3: cpu at 101 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a57";
+               reg = <0x0 0x101>;
+               enable-method = "psci";
+               cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+                                  &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+       };
+
+       CPU4: cpu at 10000 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a57";
+               reg = <0x0 0x10000>;
+               enable-method = "psci";
+               cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+                                  &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+       };
+
+       CPU5: cpu at 10001 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a57";
+               reg = <0x0 0x10001>;
+               enable-method = "psci";
+               cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+                                  &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+       };
+
+       CPU6: cpu at 10100 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a57";
+               reg = <0x0 0x10100>;
+               enable-method = "psci";
+               cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+                                  &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+       };
+
+       CPU7: cpu at 10101 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a57";
+               reg = <0x0 0x10101>;
+               enable-method = "psci";
+               cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+                                  &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+       };
+
+       CPU8: cpu at 100000000 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a53";
+               reg = <0x1 0x0>;
+               enable-method = "psci";
+               cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+                                  &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+       };
+
+       CPU9: cpu at 100000001 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a53";
+               reg = <0x1 0x1>;
+               enable-method = "psci";
+               cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+                                  &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+       };
+
+       CPU10: cpu at 100000100 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a53";
+               reg = <0x1 0x100>;
+               enable-method = "psci";
+               cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+                                  &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+       };
+
+       CPU11: cpu at 100000101 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a53";
+               reg = <0x1 0x101>;
+               enable-method = "psci";
+               cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+                                  &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+       };
+
+       CPU12: cpu at 100010000 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a53";
+               reg = <0x1 0x10000>;
+               enable-method = "psci";
+               cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+                                  &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+       };
+
+       CPU13: cpu at 100010001 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a53";
+               reg = <0x1 0x10001>;
+               enable-method = "psci";
+               cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+                                  &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+       };
+
+       CPU14: cpu at 100010100 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a53";
+               reg = <0x1 0x10100>;
+               enable-method = "psci";
+               cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+                                  &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+       };
+
+       CPU15: cpu at 100010101 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a53";
+               reg = <0x1 0x10101>;
+               enable-method = "psci";
+               cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+                                  &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+       };
+
+       idle-states {
+               entry-method = "arm,psci";
+
+               CPU_RETENTION_0_0: cpu-retention-0-0 {
+                       compatible = "arm,idle-state";
+                       arm,psci-suspend-param = <0x0010000>;
+                       entry-latency-us = <20>;
+                       exit-latency-us = <40>;
+                       min-residency-us = <80>;
+               };
+
+               CLUSTER_RETENTION_0: cluster-retention-0 {
+                       compatible = "arm,idle-state";
+                       local-timer-stop;
+                       arm,psci-suspend-param = <0x1010000>;
+                       entry-latency-us = <50>;
+                       exit-latency-us = <100>;
+                       min-residency-us = <250>;
+                       wakeup-latency-us = <130>;
+               };
+
+               CPU_SLEEP_0_0: cpu-sleep-0-0 {
+                       compatible = "arm,idle-state";
+                       local-timer-stop;
+                       arm,psci-suspend-param = <0x0010000>;
+                       entry-latency-us = <250>;
+                       exit-latency-us = <500>;
+                       min-residency-us = <950>;
+               };
+
+               CLUSTER_SLEEP_0: cluster-sleep-0 {
+                       compatible = "arm,idle-state";
+                       local-timer-stop;
+                       arm,psci-suspend-param = <0x1010000>;
+                       entry-latency-us = <600>;
+                       exit-latency-us = <1100>;
+                       min-residency-us = <2700>;
+                       wakeup-latency-us = <1500>;
+               };
+
+               CPU_RETENTION_1_0: cpu-retention-1-0 {
+                       compatible = "arm,idle-state";
+                       arm,psci-suspend-param = <0x0010000>;
+                       entry-latency-us = <20>;
+                       exit-latency-us = <40>;
+                       min-residency-us = <90>;
+               };
+
+               CLUSTER_RETENTION_1: cluster-retention-1 {
+                       compatible = "arm,idle-state";
+                       local-timer-stop;
+                       arm,psci-suspend-param = <0x1010000>;
+                       entry-latency-us = <50>;
+                       exit-latency-us = <100>;
+                       min-residency-us = <270>;
+                       wakeup-latency-us = <100>;
+               };
+
+               CPU_SLEEP_1_0: cpu-sleep-1-0 {
+                       compatible = "arm,idle-state";
+                       local-timer-stop;
+                       arm,psci-suspend-param = <0x0010000>;
+                       entry-latency-us = <70>;
+                       exit-latency-us = <100>;
+                       min-residency-us = <300>;
+                       wakeup-latency-us = <150>;
+               };
+
+               CLUSTER_SLEEP_1: cluster-sleep-1 {
+                       compatible = "arm,idle-state";
+                       local-timer-stop;
+                       arm,psci-suspend-param = <0x1010000>;
+                       entry-latency-us = <500>;
+                       exit-latency-us = <1200>;
+                       min-residency-us = <3500>;
+                       wakeup-latency-us = <1300>;
+               };
+       };
+
+};
+
+Example 2 (ARM 32-bit, 8-cpu system, two clusters):
+
+cpus {
+       #size-cells = <0>;
+       #address-cells = <1>;
+
+       CPU0: cpu at 0 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a15";
+               reg = <0x0>;
+               cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+       };
+
+       CPU1: cpu at 1 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a15";
+               reg = <0x1>;
+               cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+       };
+
+       CPU2: cpu at 2 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a15";
+               reg = <0x2>;
+               cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+       };
+
+       CPU3: cpu at 3 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a15";
+               reg = <0x3>;
+               cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+       };
+
+       CPU4: cpu at 100 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a7";
+               reg = <0x100>;
+               cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+       };
+
+       CPU5: cpu at 101 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a7";
+               reg = <0x101>;
+               cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+       };
+
+       CPU6: cpu at 102 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a7";
+               reg = <0x102>;
+               cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+       };
+
+       CPU7: cpu at 103 {
+               device_type = "cpu";
+               compatible = "arm,cortex-a7";
+               reg = <0x103>;
+               cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+       };
+
+       idle-states {
+               CPU_SLEEP_0_0: cpu-sleep-0-0 {
+                       compatible = "arm,idle-state";
+                       local-timer-stop;
+                       entry-latency-us = <200>;
+                       exit-latency-us = <100>;
+                       min-residency-us = <400>;
+                       wakeup-latency-us = <250>;
+               };
+
+               CLUSTER_SLEEP_0: cluster-sleep-0 {
+                       compatible = "arm,idle-state";
+                       local-timer-stop;
+                       entry-latency-us = <500>;
+                       exit-latency-us = <1500>;
+                       min-residency-us = <2500>;
+                       wakeup-latency-us = <1700>;
+               };
+
+               CPU_SLEEP_1_0: cpu-sleep-1-0 {
+                       compatible = "arm,idle-state";
+                       local-timer-stop;
+                       entry-latency-us = <300>;
+                       exit-latency-us = <500>;
+                       min-residency-us = <900>;
+                       wakeup-latency-us = <600>;
+               };
+
+               CLUSTER_SLEEP_1: cluster-sleep-1 {
+                       compatible = "arm,idle-state";
+                       local-timer-stop;
+                       entry-latency-us = <800>;
+                       exit-latency-us = <2000>;
+                       min-residency-us = <6500>;
+                       wakeup-latency-us = <2300>;
+               };
+       };
+
+};
+
+===========================================
+5 - References
+===========================================
+
+[1] ARM Linux Kernel documentation - CPUs bindings
+    Documentation/devicetree/bindings/arm/cpus.txt
+
+[2] ARM Linux Kernel documentation - PSCI bindings
+    Documentation/devicetree/bindings/arm/psci.txt
+
+[3] ARM Server Base System Architecture (SBSA)
+    http://infocenter.arm.com/help/index.jsp
+
+[4] ARM Architecture Reference Manuals
+    http://infocenter.arm.com/help/index.jsp
+
+[5] ePAPR standard
+    https://www.power.org/documentation/epapr-version-1-1/
Documentation/devicetree/bindings/arm/psci.txt +13 -2
index b4a58f3..5aa40ed 100644
@@ -50,6 +50,16 @@ Main node optional properties:
 
  - migrate       : Function ID for MIGRATE operation
 
+Device tree nodes that require usage of PSCI CPU_SUSPEND function (ie idle
+state nodes, as per bindings in [1]) must specify the following properties:
+
+- arm,psci-suspend-param
+               Usage: Required for state nodes[1] if the corresponding
+                       idle-states node entry-method property is set
+                       to "psci".
+               Value type: <u32>
+               Definition: power_state parameter to pass to the PSCI
+                           suspend call.
 
 Example:
 
@@ -64,7 +74,6 @@ Case 1: PSCI v0.1 only.
                migrate         = <0x95c10003>;
        };
 
-
 Case 2: PSCI v0.2 only
 
        psci {
@@ -88,3 +97,6 @@ Case 3: PSCI v0.2 and PSCI v0.1.
 
...
        };
+
+[1] Kernel documentation - ARM idle states bindings
+    Documentation/devicetree/bindings/arm/idle-states.txt
--

1.9.1

#4 lore raw

PATCH v6 1/7 Documentation: arm: define DT idle states bindings

In reply to Lorenzo Pieralisi
Quoted 26 lines · Lorenzo Pieralisi

ARM based platforms implement a variety of power management schemes that allow processors to enter idle states at run-time.
The parameters defining these idle states vary on a per-platform basis forcing the OS to hardcode the state parameters in platform specific static tables whose size grows as the number of platforms supported in the kernel increases and hampers device drivers standardization.

Therefore, this patch aims at standardizing idle state device tree bindings for ARM platforms. Bindings define idle state parameters inclusive of entry methods and state latencies, to allow operating systems to retrieve the configuration entries from the device tree and initialize the related power management drivers, paving the way for common code in the kernel to deal with idle states and removing the need for static data in current and previous kernel versions.

ARM64 platforms require the DT to define an entry-method property for idle states.

On system implementing PSCI as an enable-method to enter low-power states the PSCI CPU suspend method requires the power_state parameter to be passed to the PSCI CPU suspend function.

This parameter is specific to a power state and platform specific, therefore must be provided by firmware to the OS in order to enable proper call sequence.

Quoted 5 lines

Thus, this patch also adds a property in the PSCI bindings that describes how the PSCI CPU suspend power_state parameter should be defined in DT in all device nodes that rely on PSCI CPU suspend method usage.

Quoted 752 lines
---
  Documentation/devicetree/bindings/arm/cpus.txt     |   8 +
  .../devicetree/bindings/arm/idle-states.txt        | 679 +++++++++++++++++++++
  Documentation/devicetree/bindings/arm/psci.txt     |  14 +-
  3 files changed, 700 insertions(+), 1 deletion(-)

create mode 100644 Documentation/devicetree/bindings/arm/idle-states.txt

Documentation/devicetree/bindings/arm/cpus.txt +8 -0
index 1fe72a0..a44d4fd 100644
@@ -215,6 +215,12 @@ nodes to be present and contain the properties described below.
  		Value type: <phandle>
  		Definition: Specifies the ACC[2] node associated with this CPU.
 
+	- cpu-idle-states
+		Usage: Optional
+		Value type: <prop-encoded-array>
+		Definition:
+			# List of phandles to idle state nodes supported
+			  by this cpu [3].
 
  Example 1 (dual-cluster big.LITTLE system 32-bit):
 
@@ -411,3 +417,5 @@ cpus {
  --
  [1] arm/msm/qcom,saw2.txt
  [2] arm/msm/qcom,kpss-acc.txt
+[3] ARM Linux kernel documentation - idle states bindings
+    Documentation/devicetree/bindings/arm/idle-states.txt
Documentation/devicetree/bindings/arm/idle-states.txt new file +679 -0
new file mode 100644
index 0000000..37375c7
@@ -0,0 +1,679 @@
+==========================================
+ARM idle states binding description
+==========================================
+
+==========================================
+1 - Introduction
+==========================================
+
+ARM systems contain HW capable of managing power consumption dynamically,
+where cores can be put in different low-power states (ranging from simple
+wfi to power gating) according to OS PM policies. The CPU states representing
+the range of dynamic idle states that a processor can enter at run-time, can be
+specified through device tree bindings representing the parameters required
+to enter/exit specific idle states on a given processor.
+
+According to the Server Base System Architecture document (SBSA, [3]), the
+power states an ARM CPU can be put into are identified by the following list:
+
+- Running
+- Idle_standby
+- Idle_retention
+- Sleep
+- Off
+
+The power states described in the SBSA document define the basic CPU states on
+top of which ARM platforms implement power management schemes that allow an OS
+PM implementation to put the processor in different idle states (which include
+states listed above; "off" state is not an idle state since it does not have
+wake-up capabilities, hence it is not considered in this document).
+
+Idle state parameters (eg entry latency) are platform specific and need to be
+characterized with bindings that provide the required information to OS PM
+code so that it can build the required tables and use them at runtime.
+
+The device tree binding definition for ARM idle states is the subject of this
+document.
+
+===========================================
+2 - idle-states definitions
+===========================================
+
+Idle states are characterized for a specific system through a set of
+timing and energy related properties, that underline the HW behaviour
+triggered upon idle states entry and exit.
+
+The following diagram depicts the CPU execution phases and related timing
+properties required to enter and exit an idle state:
+
+..__[EXEC]__|__[PREP]__|__[ENTRY]__|__[IDLE]__|__[EXIT]__|__[EXEC]__..
+	    |          |           |          |          |
+
+	    |<------ entry ------->|
+	    |       latency        |
+					      |<- exit ->|
+					      |  latency |
+	    |<-------- min-residency -------->|
+		       |<-------  wakeup-latency ------->|
+
+		Diagram 1: CPU idle state execution phases
+
+EXEC:	Normal CPU execution.
+
+PREP:	Preparation phase before committing the hardware to idle mode
+	like cache flushing. This is abortable on pending wake-up
+	event conditions. The abort latency is assumed to be negligible
+	(i.e. less than the ENTRY + EXIT duration). If aborted, CPU
+	goes back to EXEC. This phase is optional. If not abortable,
+	this should be included in the ENTRY phase instead.
+
+ENTRY:	The hardware is committed to idle mode. This period must run
+	to completion up to IDLE before anything else can happen.
+
+IDLE:	This is the actual energy-saving idle period. This may last
+	between 0 and infinite time, until a wake-up event occurs.
+
+EXIT:	Period during which the CPU is brought back to operational
+	mode (EXEC).
+
+entry-latency: Worst case latency required to enter the idle state. The
+exit-latency may be guaranteed only after entry-latency has passed.
+
+min-residency: Minimum period, including preparation and entry, for a given
+idle state to be worthwhile energywise.
+
+wakeup-latency: Maximum delay between the signaling of a wake-up event and the
+CPU being able to execute normal code again. If not specified, this is assumed
+to be entry-latency + exit-latency.
+
+These timing parameters can be used by an OS in different circumstances.
+
+An idle CPU requires the expected min-residency time to select the most
+appropriate idle state based on the expected expiry time of the next IRQ
+(ie wake-up) that causes the CPU to return to the EXEC phase.
+
+An operating system scheduler may need to compute the shortest wake-up delay
+for CPUs in the system by detecting how long will it take to get a CPU out
+of an idle state, eg:
+
+wakeup-delay = exit-latency + max(entry-latency - (now - entry-timestamp), 0)
+
+In other words, the scheduler can make its scheduling decision by selecting
+(eg waking-up) the CPU with the shortest wake-up latency.
+The wake-up latency must take into account the entry latency if that period
+has not expired. The abortable nature of the PREP period can be ignored
+if it cannot be relied upon (e.g. the PREP deadline may occur much sooner than
+the worst case since it depends on the CPU operating conditions, ie caches
+state).
+
+An OS has to reliably probe the wakeup-latency since some devices can enforce
+latency constraints guarantees to work properly, so the OS has to detect the
+worst case wake-up latency it can incur if a CPU is allowed to enter an
+idle state, and possibly to prevent that to guarantee reliable device
+functioning.
+
+The min-residency time parameter deserves further explanation since it is
+expressed in time units but must factor in energy consumption coefficients.
+
+The energy consumption of a cpu when it enters a power state can be roughly
+characterised by the following graph:
+
+               |
+               |
+               |
+           e   |
+           n   |                                      /---
+           e   |                               /------
+           r   |                        /------
+           g   |                  /-----
+           y   |           /------
+               |       ----
+               |      /|
+               |     / |
+               |    /  |
+               |   /   |
+               |  /    |
+               | /     |
+               |/      |
+          -----|-------+----------------------------------
+              0|       1                              time(ms)
+
+		Graph 1: Energy vs time example
+
+The graph is split in two parts delimited by time 1ms on the X-axis.
+The graph curve with X-axis values = { x | 0 < x < 1ms } has a steep slope
+and denotes the energy costs incurred whilst entering and leaving the idle
+state.
+The graph curve in the area delimited by X-axis values = {x | x > 1ms } has
+shallower slope and essentially represents the energy consumption of the idle
+state.
+
+min-residency is defined for a given idle state as the minimum expected
+residency time for a state (inclusive of preparation and entry) after
+which choosing that state become the most energy efficient option. A good
+way to visualise this, is by taking the same graph above and comparing some
+states energy consumptions plots.
+
+For sake of simplicity, let's consider a system with two idle states IDLE1,
+and IDLE2:
+
+          |
+          |
+          |
+          |                                                  /-- IDLE1
+       e  |                                              /---
+       n  |                                         /----
+       e  |                                     /---
+       r  |                                /-----/--------- IDLE2
+       g  |                    /-------/---------
+       y  |        ------------    /---|
+          |       /           /----    |
+          |      /        /---         |
+          |     /    /----             |
+          |    / /---                  |
+          |   ---                      |
+          |  /                         |
+          | /                          |
+          |/                           |                  time
+       ---/----------------------------+------------------------
+          |IDLE1-energy < IDLE2-energy | IDLE2-energy < IDLE1-energy
+                                       |
+                                IDLE2-min-residency
+
+		Graph 2: idle states min-residency example
+
+In graph 2 above, that takes into account idle states entry/exit energy
+costs, it is clear that if the idle state residency time (ie time till next
+wake-up IRQ) is less than IDLE2-min-residency, IDLE1 is the better idle state
+choice energywise.
+
+This is mainly down to the fact that IDLE1 entry/exit energy costs are lower
+than IDLE2.
+
+However, the lower power consumption (ie shallower energy curve slope) of idle
+state IDLE2 implies that after a suitable time, IDLE2 becomes more energy
+efficient.
+
+The time at which IDLE2 becomes more energy efficient than IDLE1 (and other
+shallower states in a system with multiple idle states) is defined
+IDLE2-min-residency and corresponds to the time when energy consumption of
+IDLE1 and IDLE2 states breaks even.
+
+The definitions provided in this section underpin the idle states
+properties specification that is the subject of the following sections.
+
+===========================================
+3 - idle-states node
+===========================================
+
+ARM processor idle states are defined within the idle-states node, which is
+a direct child of the cpus node [1] and provides a container where the
+processor idle states, defined as device tree nodes, are listed.
+
+- idle-states node
+
+	Usage: Optional - On ARM systems, it is a container of processor idle
+			  states nodes. If the system does not provide CPU
+			  power management capabilities or the processor just
+			  supports idle_standby an idle-states node is not
+			  required.
+
+	Description: idle-states node is a container node, where its
+		     subnodes describe the CPU idle states.
+
+	Node name must be "idle-states".
+
+	The idle-states node's parent node must be the cpus node.
+
+	The idle-states node's child nodes can be:
+
+	- one or more state nodes
+
+	Any other configuration is considered invalid.
+
+	An idle-states node defines the following properties:
+
+	- entry-method
+		Value type: <stringlist>
+		Usage and definition depend on ARM architecture version.
+			# On ARM v8 64-bit this property is required and must
+			  be one of:
+			   - "psci" (see bindings in [2])
+			# On ARM 32-bit systems this property is optional
+
+The nodes describing the idle states (state) can only be defined within the
+idle-states node, any other configuration is considered invalid and therefore
+must be ignored.
+
+===========================================
+4 - state node
+===========================================
+
+A state node represents an idle state description and must be defined as
+follows:
+
+- state node
+
+	Description: must be child of the idle-states node
+
+	The state node name shall follow standard device tree naming
+	rules ([5], 2.2.1 "Node names"), in particular state nodes which
+	are siblings within a single common parent must be given a unique name.
+
+	The idle state entered by executing the wfi instruction (idle_standby
+	SBSA,[3][4]) is considered standard on all ARM platforms and therefore
+	must not be listed.
+
+	With the definitions provided above, the following list represents
+	the valid properties for a state node:
+
+	- compatible
+		Usage: Required
+		Value type: <stringlist>
+		Definition: Must be "arm,idle-state".
+
+	- local-timer-stop
+		Usage: See definition
+		Value type: <none>
+		Definition: if present the CPU local timer control logic is
+			    lost on state entry, otherwise it is retained.
+
+	- entry-latency-us
+		Usage: Required
+		Value type: <prop-encoded-array>
+		Definition: u32 value representing worst case latency in
+			    microseconds required to enter the idle state.
+			    The exit-latency-us duration may be guaranteed
+			    only after entry-latency-us has passed.
+
+	- exit-latency-us
+		Usage: Required
+		Value type: <prop-encoded-array>
+		Definition: u32 value representing worst case latency
+			    in microseconds required to exit the idle state.
+
+	- min-residency-us
+		Usage: Required
+		Value type: <prop-encoded-array>
+		Definition: u32 value representing minimum residency duration
+			    in microseconds, inclusive of preparation and
+			    entry, for this idle state to be considered
+			    worthwhile energy wise (refer to section 2 of
+			    this document for a complete description).
+
+	- wakeup-latency-us:
+		Usage: Optional
+		Value type: <prop-encoded-array>
+		Definition: u32 value representing maximum delay between the
+			    signaling of a wake-up event and the CPU being
+			    able to execute normal code again. If omitted,
+			    this is assumed to be equal to:
+
+				entry-latency-us + exit-latency-us
+
+			    It is important to supply this value on systems
+			    where the duration of PREP phase (see diagram 1,
+			    section 2) is non-neglibigle.
+			    In such systems entry-latency-us + exit-latency-us
+			    will exceed wakeup-latency-us by this duration.
+
+	In addition to the properties listed above, a state node may require
+	additional properties specifics to the entry-method defined in the
+	idle-states node, please refer to the entry-method bindings
+	documentation for properties definitions.
+
+===========================================
+4 - Examples
+===========================================
+
+Example 1 (ARM 64-bit, 16-cpu system, PSCI enable-method):
+
+cpus {
+	#size-cells = <0>;
+	#address-cells = <2>;
+
+	CPU0: cpu at 0 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a57";
+		reg = <0x0 0x0>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+				   &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU1: cpu at 1 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a57";
+		reg = <0x0 0x1>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+				   &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU2: cpu at 100 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a57";
+		reg = <0x0 0x100>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+				   &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU3: cpu at 101 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a57";
+		reg = <0x0 0x101>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+				   &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU4: cpu at 10000 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a57";
+		reg = <0x0 0x10000>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+				   &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU5: cpu at 10001 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a57";
+		reg = <0x0 0x10001>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+				   &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU6: cpu at 10100 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a57";
+		reg = <0x0 0x10100>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+				   &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU7: cpu at 10101 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a57";
+		reg = <0x0 0x10101>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0
+				   &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU8: cpu at 100000000 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a53";
+		reg = <0x1 0x0>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+				   &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU9: cpu at 100000001 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a53";
+		reg = <0x1 0x1>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+				   &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU10: cpu at 100000100 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a53";
+		reg = <0x1 0x100>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+				   &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU11: cpu at 100000101 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a53";
+		reg = <0x1 0x101>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+				   &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU12: cpu at 100010000 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a53";
+		reg = <0x1 0x10000>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+				   &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU13: cpu at 100010001 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a53";
+		reg = <0x1 0x10001>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+				   &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU14: cpu at 100010100 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a53";
+		reg = <0x1 0x10100>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+				   &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU15: cpu at 100010101 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a53";
+		reg = <0x1 0x10101>;
+		enable-method = "psci";
+		cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0
+				   &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>;
+	};
+
+	idle-states {
+		entry-method = "arm,psci";
+
+		CPU_RETENTION_0_0: cpu-retention-0-0 {
+			compatible = "arm,idle-state";
+			arm,psci-suspend-param = <0x0010000>;
+			entry-latency-us = <20>;
+			exit-latency-us = <40>;
+			min-residency-us = <80>;
+		};
+
+		CLUSTER_RETENTION_0: cluster-retention-0 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			arm,psci-suspend-param = <0x1010000>;
+			entry-latency-us = <50>;
+			exit-latency-us = <100>;
+			min-residency-us = <250>;
+			wakeup-latency-us = <130>;
+		};
+
+		CPU_SLEEP_0_0: cpu-sleep-0-0 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			arm,psci-suspend-param = <0x0010000>;
+			entry-latency-us = <250>;
+			exit-latency-us = <500>;
+			min-residency-us = <950>;
+		};
+
+		CLUSTER_SLEEP_0: cluster-sleep-0 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			arm,psci-suspend-param = <0x1010000>;
+			entry-latency-us = <600>;
+			exit-latency-us = <1100>;
+			min-residency-us = <2700>;
+			wakeup-latency-us = <1500>;
+		};
+
+		CPU_RETENTION_1_0: cpu-retention-1-0 {
+			compatible = "arm,idle-state";
+			arm,psci-suspend-param = <0x0010000>;
+			entry-latency-us = <20>;
+			exit-latency-us = <40>;
+			min-residency-us = <90>;
+		};
+
+		CLUSTER_RETENTION_1: cluster-retention-1 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			arm,psci-suspend-param = <0x1010000>;
+			entry-latency-us = <50>;
+			exit-latency-us = <100>;
+			min-residency-us = <270>;
+			wakeup-latency-us = <100>;
+		};
+
+		CPU_SLEEP_1_0: cpu-sleep-1-0 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			arm,psci-suspend-param = <0x0010000>;
+			entry-latency-us = <70>;
+			exit-latency-us = <100>;
+			min-residency-us = <300>;
+			wakeup-latency-us = <150>;
+		};
+
+		CLUSTER_SLEEP_1: cluster-sleep-1 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			arm,psci-suspend-param = <0x1010000>;
+			entry-latency-us = <500>;
+			exit-latency-us = <1200>;
+			min-residency-us = <3500>;
+			wakeup-latency-us = <1300>;
+		};
+	};
+
+};
+
+Example 2 (ARM 32-bit, 8-cpu system, two clusters):
+
+cpus {
+	#size-cells = <0>;
+	#address-cells = <1>;
+
+	CPU0: cpu at 0 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a15";
+		reg = <0x0>;
+		cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU1: cpu at 1 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a15";
+		reg = <0x1>;
+		cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU2: cpu at 2 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a15";
+		reg = <0x2>;
+		cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU3: cpu at 3 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a15";
+		reg = <0x3>;
+		cpu-idle-states = <&CPU_SLEEP_0_0 &CLUSTER_SLEEP_0>;
+	};
+
+	CPU4: cpu at 100 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a7";
+		reg = <0x100>;
+		cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU5: cpu at 101 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a7";
+		reg = <0x101>;
+		cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU6: cpu at 102 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a7";
+		reg = <0x102>;
+		cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+	};
+
+	CPU7: cpu at 103 {
+		device_type = "cpu";
+		compatible = "arm,cortex-a7";
+		reg = <0x103>;
+		cpu-idle-states = <&CPU_SLEEP_1_0 &CLUSTER_SLEEP_1>;
+	};
+
+	idle-states {
+		CPU_SLEEP_0_0: cpu-sleep-0-0 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			entry-latency-us = <200>;
+			exit-latency-us = <100>;
+			min-residency-us = <400>;
+			wakeup-latency-us = <250>;
+		};
+
+		CLUSTER_SLEEP_0: cluster-sleep-0 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			entry-latency-us = <500>;
+			exit-latency-us = <1500>;
+			min-residency-us = <2500>;
+			wakeup-latency-us = <1700>;
+		};
+
+		CPU_SLEEP_1_0: cpu-sleep-1-0 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			entry-latency-us = <300>;
+			exit-latency-us = <500>;
+			min-residency-us = <900>;
+			wakeup-latency-us = <600>;
+		};
+
+		CLUSTER_SLEEP_1: cluster-sleep-1 {
+			compatible = "arm,idle-state";
+			local-timer-stop;
+			entry-latency-us = <800>;
+			exit-latency-us = <2000>;
+			min-residency-us = <6500>;
+			wakeup-latency-us = <2300>;
+		};
+	};
+
+};
+
+===========================================
+5 - References
+===========================================
+
+[1] ARM Linux Kernel documentation - CPUs bindings
+    Documentation/devicetree/bindings/arm/cpus.txt
+
+[2] ARM Linux Kernel documentation - PSCI bindings
+    Documentation/devicetree/bindings/arm/psci.txt
+
+[3] ARM Server Base System Architecture (SBSA)
+    http://infocenter.arm.com/help/index.jsp
+
+[4] ARM Architecture Reference Manuals
+    http://infocenter.arm.com/help/index.jsp
+
+[5] ePAPR standard
+    https://www.power.org/documentation/epapr-version-1-1/
Documentation/devicetree/bindings/arm/psci.txt +13 -1
index b4a58f3..5aa40ed 100644
@@ -50,6 +50,16 @@ Main node optional properties:
 
   - migrate       : Function ID for MIGRATE operation
 
+Device tree nodes that require usage of PSCI CPU_SUSPEND function (ie idle
+state nodes, as per bindings in [1]) must specify the following properties:
+
+- arm,psci-suspend-param
+		Usage: Required for state nodes[1] if the corresponding
+                       idle-states node entry-method property is set
+                       to "psci".
+		Value type: <u32>
+		Definition: power_state parameter to pass to the PSCI
+			    suspend call.
 
  Example:
 
@@ -64,7 +74,6 @@ Case 1: PSCI v0.1 only.
  		migrate		= <0x95c10003>;
  	};
 
-
  Case 2: PSCI v0.2 only
 
  	psci {
@@ -88,3 +97,6 @@ Case 3: PSCI v0.2 and PSCI v0.1.
 
...
  	};
+
+[1] Kernel documentation - ARM idle states bindings
+    Documentation/devicetree/bindings/arm/idle-states.txt
Signature · 5 lines
  <http://www.linaro.org/> Linaro.org ? Open source software for ARM SoCs

Follow Linaro:  <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
#5 lore raw

PATCH v6 2/7 drivers: cpuidle: implement DT based idle states infrastructure

In reply to Lorenzo Pieralisi

On most common ARM systems, the low-power states a CPU can be put into are not discoverable in HW and require device tree bindings to describe power down suspend operations and idle states parameters.

In order to enable DT based idle states and configure idle drivers, this patch implements the bulk infrastructure required to parse the device tree idle states bindings and initialize the corresponding CPUidle driver states data.

The parsing API accepts a start index that defines the first idle state that should be initialized by the parsing code in order to give new and legacy driver flexibility over which states should be parsed using the new DT mechanism.

The idle states list is obtained from the first cpu in the driver cpumask, which implicitly means the parsing code expects idle states (and related list of phandles) to be the same for all CPUs in the CPUidle driver mask. The kernel does not check this assumption, it must be enforced by the bootloader to ensure correct system behaviour.

---
 drivers/cpuidle/Kconfig          |   8 +++
 drivers/cpuidle/Makefile         |   1 +
 drivers/cpuidle/dt_idle_states.c | 138 +++++++++++++++++++++++++++++++++++++++
 drivers/cpuidle/dt_idle_states.h |   5 ++
 4 files changed, 152 insertions(+)
 create mode 100644 drivers/cpuidle/dt_idle_states.c
 create mode 100644 drivers/cpuidle/dt_idle_states.h
drivers/cpuidle/Kconfig +8 -0
index 1b96fb9..414e7a96 100644
@@ -30,6 +30,14 @@ config CPU_IDLE_GOV_MENU
 	bool "Menu governor (for tickless system)"
 	default y
 
+config DT_IDLE_STATES
+        bool "Idle states DT support"
+	depends on ARM || ARM64
+	help
+	 Allows the CPU idle framework to initialize CPU idle drivers
+	 state data by using DT provided nodes compliant with idle states
+	 device tree bindings.
+
 menu "ARM CPU Idle Drivers"
 depends on ARM
 source "drivers/cpuidle/Kconfig.arm"
drivers/cpuidle/Makefile +1 -0
index d8bb1ff..b27a062 100644
@@ -4,6 +4,7 @@
 
 obj-y += cpuidle.o driver.o governor.o sysfs.o governors/
 obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
+obj-$(CONFIG_DT_IDLE_STATES)		  += dt_idle_states.o
 
 ##################################################################################
 # ARM SoC drivers
drivers/cpuidle/dt_idle_states.c new file +138 -0

138 lines not on this page · raw message

drivers/cpuidle/dt_idle_states.h new file +5 -0
new file mode 100644
index 0000000..728c37c
@@ -0,0 +1,5 @@
+#ifndef __DT_IDLE_STATES
+#define __DT_IDLE_STATES
+
+int dt_init_idle_driver(struct cpuidle_driver *drv, unsigned int start_idx);
+#endif
Signature · 1 line
1.9.1
#6 lore raw

PATCH v6 2/7 drivers: cpuidle: implement DT based idle states infrastructure

Quoted 14 lines · Lorenzo Pieralisi

On most common ARM systems, the low-power states a CPU can be put into are not discoverable in HW and require device tree bindings to describe power down suspend operations and idle states parameters.

In order to enable DT based idle states and configure idle drivers, this patch implements the bulk infrastructure required to parse the device tree idle states bindings and initialize the corresponding CPUidle driver states data.

The parsing API accepts a start index that defines the first idle state that should be initialized by the parsing code in order to give new and legacy driver flexibility over which states should be parsed using the new DT mechanism.

Quoted 7 lines

The idle states list is obtained from the first cpu in the driver cpumask, which implicitly means the parsing code expects idle states (and related list of phandles) to be the same for all CPUs in the CPUidle driver mask. The kernel does not check this assumption, it must be enforced by the bootloader to ensure correct system behaviour.

This patch looks good for me but I have a couple of questions below.

Quoted 15 lines
---
  drivers/cpuidle/Kconfig          |   8 +++
  drivers/cpuidle/Makefile         |   1 +
  drivers/cpuidle/dt_idle_states.c | 138 +++++++++++++++++++++++++++++++++++++++
  drivers/cpuidle/dt_idle_states.h |   5 ++
  4 files changed, 152 insertions(+)

create mode 100644 drivers/cpuidle/dt_idle_states.c create mode 100644 drivers/cpuidle/dt_idle_states.h

drivers/cpuidle/Kconfig +0 -0
index 1b96fb9..414e7a96 100644
@@ -30,6 +30,14 @@ config CPU_IDLE_GOV_MENU
  	bool "Menu governor (for tickless system)"

drivers/cpuidle/Kconfig config CPU_IDLE_GOV_MENU · line 40

Quoted 10 lines
drivers/cpuidle/Kconfig +8 -0
  	default y
 
+config DT_IDLE_STATES
+        bool "Idle states DT support"
+	depends on ARM || ARM64
+	help
+	 Allows the CPU idle framework to initialize CPU idle drivers
+	 state data by using DT provided nodes compliant with idle states
+	 device tree bindings.
+

Wouldn't make sense to make this as an hidden option and let the different drivers to set DT_IDLE_STATES if they depend on it ?

Quoted 133 lines
drivers/cpuidle/Kconfig +0 -0
  menu "ARM CPU Idle Drivers"
  depends on ARM
  source "drivers/cpuidle/Kconfig.arm"
drivers/cpuidle/Makefile +1 -0
index d8bb1ff..b27a062 100644
@@ -4,6 +4,7 @@
 
  obj-y += cpuidle.o driver.o governor.o sysfs.o governors/
  obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
+obj-$(CONFIG_DT_IDLE_STATES)		  += dt_idle_states.o
 
  ##################################################################################
  # ARM SoC drivers
drivers/cpuidle/dt_idle_states.c new file +112 -0
new file mode 100644
index 0000000..5413132
@@ -0,0 +1,138 @@
+/*
+ * DT idle states parsing code.
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ * Author: Lorenzo Pieralisi <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) "DT idle-states: " fmt
+
+#include <linux/cpuidle.h>
+#include <linux/cpumask.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include "dt_idle_states.h"
+
+static int init_state_node(struct cpuidle_state *idle_state,
+			   struct device_node *state_node)
+{
+	int err;
+
+	err = of_property_read_u32(state_node, "wakeup-latency-us",
+				   &idle_state->exit_latency);
+	if (err) {
+		u32 entry_latency, exit_latency;
+
+		err = of_property_read_u32(state_node, "entry-latency-us",
+					   &entry_latency);
+		if (err) {
+			pr_debug(" * %s missing entry-latency-us property\n",
+				 state_node->full_name);
+			return -EINVAL;
+		}
+
+		err = of_property_read_u32(state_node, "exit-latency-us",
+					   &exit_latency);
+		if (err) {
+			pr_debug(" * %s missing exit-latency-us property\n",
+				 state_node->full_name);
+			return -EINVAL;
+		}
+		/*
+		 * If wakeup-latency-us is missing, default to entry+exit
+		 * latencies as defined in idle states bindings
+		 */
+		idle_state->exit_latency = entry_latency + exit_latency;
+	}
+
+	err = of_property_read_u32(state_node, "min-residency-us",
+				   &idle_state->target_residency);
+	if (err) {
+		pr_debug(" * %s missing min-residency-us property\n",
+			     state_node->full_name);
+		return -EINVAL;
+	}
+
+	idle_state->flags = CPUIDLE_FLAG_TIME_VALID;
+	if (of_property_read_bool(state_node, "local-timer-stop"))
+		idle_state->flags |= CPUIDLE_FLAG_TIMER_STOP;
+	/*
+	 * TODO:
+	 *	replace with kstrdup and pointer assignment when name
+	 *	and desc become string pointers
+	 */
+	strncpy(idle_state->name, state_node->name, CPUIDLE_NAME_LEN - 1);
+	strncpy(idle_state->desc, state_node->name, CPUIDLE_DESC_LEN - 1);
+	return 0;
+}
+
+/**
+ * dt_init_idle_driver() - Parse the DT idle states and initialize the
+ *			   idle driver states array
+ *
+ * @drv:	  Pointer to CPU idle driver to be initialized
+ * @start_idx:    First idle state index to be initialized
+ *
+ * On success the states array in the cpuidle driver contains
+ * initialized entries in the states array, starting from index start_idx.
+ *
+ * Return:
+ *	0 on success
+ *	<0 on failure
+ */
+int dt_init_idle_driver(struct cpuidle_driver *drv, unsigned int start_idx)
+{
+	unsigned int i, state_idx = start_idx;
+	struct cpuidle_state *idle_state;
+	struct device_node *state_node, *cpu_node;
+
+
+	if (state_idx >= CPUIDLE_STATE_MAX)
+		return -EINVAL;
+	/*
+	 * We get the idle states for the first logical cpu in the
+	 * driver mask. The kernel does not check idle states on all
+	 * cpus in the driver mask, they are assumed to be the same
+	 * by default.
+	 */
+	cpu_node = of_cpu_device_node_get(cpumask_first(drv->cpumask));
+
+	for (i = 0; ; i++) {
+		int err;
+
+		state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
+		if (!state_node)

drivers/cpuidle/dt_idle_states.c line 122

Quoted 10 lines
drivers/cpuidle/dt_idle_states.c new file +10 -0
+			break;
+
+		if (state_idx == CPUIDLE_STATE_MAX) {
+			pr_warn("State index reached static CPU idle driver states array size\n");
+			break;
+		}
+
+		idle_state = &drv->states[state_idx++];
+		err = init_state_node(idle_state, state_node);
+		if (err)

As the init_state_node error traces are in pr_debug level, a pr_err would help here IMO.

Quoted 28 lines
drivers/cpuidle/dt_idle_states.c new file +16 -0
+			return err;
+	}
+
+	/*
+	 * If no idle states are detected, return an error and let the idle
+	 * driver initialization fail accordingly since initializing a driver
+	 * with simple WFI as an idle state is equivalent to letting the
+	 * kernel run the default idle loop.
+	 */
+	if (!i)
+		return -ENODATA;
+
+	drv->state_count = state_idx;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dt_init_idle_driver);
drivers/cpuidle/dt_idle_states.h new file +5 -0
new file mode 100644
index 0000000..728c37c
@@ -0,0 +1,5 @@
+#ifndef __DT_IDLE_STATES
+#define __DT_IDLE_STATES
+
+int dt_init_idle_driver(struct cpuidle_driver *drv, unsigned int start_idx);
+#endif
Signature · 5 lines
  <http://www.linaro.org/> Linaro.org ? Open source software for ARM SoCs

Follow Linaro:  <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
#7 lore raw

PATCH v6 2/7 drivers: cpuidle: implement DT based idle states infrastructure

Quoted 42 lines · Daniel Lezcano
Quoted 14 lines · Lorenzo Pieralisi

On most common ARM systems, the low-power states a CPU can be put into are not discoverable in HW and require device tree bindings to describe power down suspend operations and idle states parameters.

In order to enable DT based idle states and configure idle drivers, this patch implements the bulk infrastructure required to parse the device tree idle states bindings and initialize the corresponding CPUidle driver states data.

The parsing API accepts a start index that defines the first idle state that should be initialized by the parsing code in order to give new and legacy driver flexibility over which states should be parsed using the new DT mechanism.

Quoted 7 lines

The idle states list is obtained from the first cpu in the driver cpumask, which implicitly means the parsing code expects idle states (and related list of phandles) to be the same for all CPUs in the CPUidle driver mask. The kernel does not check this assumption, it must be enforced by the bootloader to ensure correct system behaviour.

This patch looks good for me but I have a couple of questions below.

Quoted 17 lines
---
  drivers/cpuidle/Kconfig          |   8 +++
  drivers/cpuidle/Makefile         |   1 +
  drivers/cpuidle/dt_idle_states.c | 138 +++++++++++++++++++++++++++++++++++++++
  drivers/cpuidle/dt_idle_states.h |   5 ++
  4 files changed, 152 insertions(+)

create mode 100644 drivers/cpuidle/dt_idle_states.c create mode 100644 drivers/cpuidle/dt_idle_states.h

drivers/cpuidle/Kconfig +0 -0
index 1b96fb9..414e7a96 100644
@@ -30,6 +30,14 @@ config CPU_IDLE_GOV_MENU
  	bool "Menu governor (for tickless system)"
  	default y
 
Quoted 11 lines

drivers/cpuidle/Kconfig config CPU_IDLE_GOV_MENU · line 40

Quoted 8 lines
drivers/cpuidle/Kconfig +8 -0
+config DT_IDLE_STATES
+        bool "Idle states DT support"
+	depends on ARM || ARM64
+	help
+	 Allows the CPU idle framework to initialize CPU idle drivers
+	 state data by using DT provided nodes compliant with idle states
+	 device tree bindings.
+

Wouldn't make sense to make this as an hidden option and let the different drivers to set DT_IDLE_STATES if they depend on it ?

Yes, it would :)

[...]

Quoted 41 lines
Quoted 41 lines
drivers/cpuidle/Kconfig +41 -0
+/**
+ * dt_init_idle_driver() - Parse the DT idle states and initialize the
+ *			   idle driver states array
+ *
+ * @drv:	  Pointer to CPU idle driver to be initialized
+ * @start_idx:    First idle state index to be initialized
+ *
+ * On success the states array in the cpuidle driver contains
+ * initialized entries in the states array, starting from index start_idx.
+ *
+ * Return:
+ *	0 on success
+ *	<0 on failure
+ */
+int dt_init_idle_driver(struct cpuidle_driver *drv, unsigned int start_idx)
+{
+	unsigned int i, state_idx = start_idx;
+	struct cpuidle_state *idle_state;
+	struct device_node *state_node, *cpu_node;
+
+
+	if (state_idx >= CPUIDLE_STATE_MAX)
+		return -EINVAL;
+	/*
+	 * We get the idle states for the first logical cpu in the
+	 * driver mask. The kernel does not check idle states on all
+	 * cpus in the driver mask, they are assumed to be the same
+	 * by default.
+	 */
+	cpu_node = of_cpu_device_node_get(cpumask_first(drv->cpumask));
+
+	for (i = 0; ; i++) {
+		int err;
+
+		state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
+		if (!state_node)
+			break;
+
+		if (state_idx == CPUIDLE_STATE_MAX) {
+			pr_warn("State index reached static CPU idle driver states array size\n");
+			break;
Quoted 8 lines

drivers/cpuidle/Kconfig

Quoted 5 lines
drivers/cpuidle/Kconfig +5 -0
+		}
+
+		idle_state = &drv->states[state_idx++];
+		err = init_state_node(idle_state, state_node);
+		if (err)

As the init_state_node error traces are in pr_debug level, a pr_err would help here IMO.

I think I agree, since at this point we have an idle state phandle and we verified that the node it points at contains invalid bindings, so it is fair to flag this up.

I will add it.

Thanks !
Lorenzo

#8 lore raw

PATCH v6 2/7 drivers: cpuidle: implement DT based idle states infrastructure

In reply to Lorenzo Pieralisi

Hi Lorenzo,

Quoted 20 lines · Lorenzo Pieralisi
drivers/cpuidle/dt_idle_states.c new file +20 -0
+/**
+ * dt_init_idle_driver() - Parse the DT idle states and initialize the
+ *                        idle driver states array
+ *
+ * @drv:         Pointer to CPU idle driver to be initialized
+ * @start_idx:    First idle state index to be initialized
+ *
+ * On success the states array in the cpuidle driver contains
+ * initialized entries in the states array, starting from index start_idx.
+ *
+ * Return:
+ *     0 on success
+ *     <0 on failure
+ */
+int dt_init_idle_driver(struct cpuidle_driver *drv, unsigned int start_idx)
+{
+       unsigned int i, state_idx = start_idx;
+       struct cpuidle_state *idle_state;
+       struct device_node *state_node, *cpu_node;
+

drivers/cpuidle/dt_idle_states.c line 106

Quoted 10 lines
drivers/cpuidle/dt_idle_states.c new file +10 -0
+
+       if (state_idx >= CPUIDLE_STATE_MAX)
+               return -EINVAL;
+       /*
+        * We get the idle states for the first logical cpu in the
+        * driver mask. The kernel does not check idle states on all
+        * cpus in the driver mask, they are assumed to be the same
+        * by default.
+        */
+       cpu_node = of_cpu_device_node_get(cpumask_first(drv->cpumask));

Is this an assumption for the short term? My understanding from the corresponding ACPI discussions is that the order of idle states may not necessarily be same for all CPUs, even for big.Little?

Cheers,
Ashwin

#9 lore raw

PATCH v6 2/7 drivers: cpuidle: implement DT based idle states infrastructure

Hi Ashwin,

Quoted 29 lines · Ashwin Chaugule

Hi Lorenzo,

Quoted 26 lines · Lorenzo Pieralisi
drivers/cpuidle/dt_idle_states.c new file +26 -0
+/**
+ * dt_init_idle_driver() - Parse the DT idle states and initialize the
+ *                        idle driver states array
+ *
+ * @drv:         Pointer to CPU idle driver to be initialized
+ * @start_idx:    First idle state index to be initialized
+ *
+ * On success the states array in the cpuidle driver contains
+ * initialized entries in the states array, starting from index start_idx.
+ *
+ * Return:
+ *     0 on success
+ *     <0 on failure
+ */
+int dt_init_idle_driver(struct cpuidle_driver *drv, unsigned int start_idx)
+{
+       unsigned int i, state_idx = start_idx;
+       struct cpuidle_state *idle_state;
+       struct device_node *state_node, *cpu_node;
+
+
+       if (state_idx >= CPUIDLE_STATE_MAX)
+               return -EINVAL;
+       /*
+        * We get the idle states for the first logical cpu in the
+        * driver mask. The kernel does not check idle states on all
Quoted 8 lines

drivers/cpuidle/dt_idle_states.c line 106

Quoted 4 lines
drivers/cpuidle/dt_idle_states.c new file +4 -0
+        * cpus in the driver mask, they are assumed to be the same
+        * by default.
+        */
+       cpu_node = of_cpu_device_node_get(cpumask_first(drv->cpumask));

Is this an assumption for the short term? My understanding from the corresponding ACPI discussions is that the order of idle states may not necessarily be same for all CPUs, even for big.Little?

Well, it is more of a problem of the CPUidle infrastructure than related to this specific patchset. The idle states for a specific cpumask (ie CPUidle driver) are expected to be all the same, same order same data.

Current driver can be used on homegeneous ARM64 SMP systems, but I am fairly confident we can extend it for big.LITTLE and also other funky SMP configurations, the parsing code only expects all cpus in the mask to have the same set of idle states (and so does the CPUidle core), if that's not the case, the cpus belong in different CPUidle drivers (ie a CPUidle driver is a set of states valid on a cpumask).

So my answer is yes, it is a proper assumption and not only for the short term, but in general.

HTH,
Lorenzo

#10 lore raw

PATCH v6 2/7 drivers: cpuidle: implement DT based idle states infrastructure

Hi Lorenzo,

Quoted 46 lines · Lorenzo Pieralisi
Quoted 26 lines
Quoted 26 lines
drivers/cpuidle/dt_idle_states.c new file +26 -0
+/**
+ * dt_init_idle_driver() - Parse the DT idle states and initialize the
+ *                        idle driver states array
+ *
+ * @drv:         Pointer to CPU idle driver to be initialized
+ * @start_idx:    First idle state index to be initialized
+ *
+ * On success the states array in the cpuidle driver contains
+ * initialized entries in the states array, starting from index start_idx.
+ *
+ * Return:
+ *     0 on success
+ *     <0 on failure
+ */
+int dt_init_idle_driver(struct cpuidle_driver *drv, unsigned int start_idx)
+{
+       unsigned int i, state_idx = start_idx;
+       struct cpuidle_state *idle_state;
+       struct device_node *state_node, *cpu_node;
+
+
+       if (state_idx >= CPUIDLE_STATE_MAX)
+               return -EINVAL;
+       /*
+        * We get the idle states for the first logical cpu in the
+        * driver mask. The kernel does not check idle states on all
Quoted 8 lines

drivers/cpuidle/dt_idle_states.c line 106

Quoted 4 lines
drivers/cpuidle/dt_idle_states.c new file +4 -0
+        * cpus in the driver mask, they are assumed to be the same
+        * by default.
+        */
+       cpu_node = of_cpu_device_node_get(cpumask_first(drv->cpumask));

Is this an assumption for the short term? My understanding from the corresponding ACPI discussions is that the order of idle states may not necessarily be same for all CPUs, even for big.Little?

Well, it is more of a problem of the CPUidle infrastructure than related to this specific patchset. The idle states for a specific cpumask (ie CPUidle driver) are expected to be all the same, same order same data.

Current driver can be used on homegeneous ARM64 SMP systems, but I am fairly confident we can extend it for big.LITTLE and also other funky SMP configurations, the parsing code only expects all cpus in the mask to have the same set of idle states (and so does the CPUidle core), if that's not the case, the cpus belong in different CPUidle drivers (ie a CPUidle driver is a set of states valid on a cpumask).

Quoted 5 lines

So my answer is yes, it is a proper assumption and not only for the short term, but in general.

HTH,
Lorenzo

Ok. Thanks for the explanation. I just had a look at drivers/cpuidle/cpuidle-big_little.c. So, you'll need another call to cpuidle_register() should there be another cpumask with a different set of idle states.

Cheers,
Ashwin

#11 lore raw

PATCH v6 3/7 arm64: kernel: introduce cpu_init_idle CPU operation

In reply to Lorenzo Pieralisi

The CPUidle subsystem on ARM64 machines requires the idle states implementation back-end to initialize idle states parameter upon boot. This patch adds a hook in the CPU operations structure that should be initialized by the CPU operations back-end in order to provide a function that initializes cpu idle states.

This patch also adds the infrastructure to arm64 kernel required to export the CPU operations based initialization interface, so that drivers (ie CPUidle) can use it when they are initialized at probe time.

---
 arch/arm64/include/asm/cpu_ops.h |  1 +
 arch/arm64/include/asm/cpuidle.h | 13 +++++++++++++
 arch/arm64/kernel/Makefile       |  1 +
 arch/arm64/kernel/cpuidle.c      | 27 +++++++++++++++++++++++++++
 4 files changed, 42 insertions(+)
 create mode 100644 arch/arm64/include/asm/cpuidle.h
 create mode 100644 arch/arm64/kernel/cpuidle.c
arch/arm64/include/asm/cpu_ops.h +1 -0
index d7b4b38..c156498 100644
@@ -47,6 +47,7 @@ struct device_node;
 struct cpu_operations {
 	const char	*name;
 	int		(*cpu_init)(struct device_node *, unsigned int);
+	int		(*cpu_init_idle)(struct device_node *, unsigned int);
 	int		(*cpu_prepare)(unsigned int);
 	int		(*cpu_boot)(unsigned int);
 	void		(*cpu_postboot)(void);
arch/arm64/include/asm/cpuidle.h new file +13 -0
new file mode 100644
index 0000000..b52a993
@@ -0,0 +1,13 @@
+#ifndef __ASM_CPUIDLE_H
+#define __ASM_CPUIDLE_H
+
+#ifdef CONFIG_CPU_IDLE
+extern int cpu_init_idle(unsigned int cpu);
+#else
+static inline int cpu_init_idle(unsigned int cpu)
+{
+	return -EOPNOTSUPP;
+}
+#endif
+
+#endif
arch/arm64/kernel/Makefile +1 -0
index cdaedad..7b0c068 100644
@@ -26,6 +26,7 @@ arm64-obj-$(CONFIG_PERF_EVENTS)		+= perf_regs.o
 arm64-obj-$(CONFIG_HW_PERF_EVENTS)	+= perf_event.o
 arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)	+= hw_breakpoint.o
 arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND)	+= sleep.o suspend.o
+arm64-obj-$(CONFIG_CPU_IDLE)		+= cpuidle.o
 arm64-obj-$(CONFIG_JUMP_LABEL)		+= jump_label.o
 arm64-obj-$(CONFIG_KGDB)		+= kgdb.o
 arm64-obj-$(CONFIG_EFI)			+= efi.o efi-stub.o efi-entry.o
arch/arm64/kernel/cpuidle.c new file +27 -0
new file mode 100644
index 0000000..46eb3ea
@@ -0,0 +1,27 @@
+/*
+ * ARM64 CPU idle arch support
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ * Author: Lorenzo Pieralisi <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <asm/cpu_ops.h>
+
+int cpu_init_idle(unsigned int cpu)
+{
+	struct device_node *cpu_node = of_cpu_device_node_get(cpu);
+
+	if (!cpu_node)
+		return -ENODEV;
+
+	if (cpu_ops[cpu] && cpu_ops[cpu]->cpu_init_idle)
+		return cpu_ops[cpu]->cpu_init_idle(cpu_node, cpu);
+
+	return -EOPNOTSUPP;
+}
Signature · 1 line
1.9.1
#12 lore raw

PATCH v6 3/7 arm64: kernel: introduce cpu_init_idle CPU operation

Quoted 12 lines · Lorenzo Pieralisi
arch/arm64/kernel/cpuidle.c new file +6 -0
new file mode 100644
index 0000000..46eb3ea
@@ -0,0 +1,27 @@
+/*
+ * ARM64 CPU idle arch support
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ * Author: Lorenzo Pieralisi <[email protected]>
+ *

arch/arm64/kernel/cpuidle.c line 16

Quoted 10 lines
arch/arm64/kernel/cpuidle.c new file +10 -0
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <asm/cpu_ops.h>
+
+int cpu_init_idle(unsigned int cpu)

sparse will probably complain here unless we include <asm/cpuidle.h> right?

Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
#13 lore raw

PATCH v6 3/7 arm64: kernel: introduce cpu_init_idle CPU operation

Quoted 17 lines · Stephen Boyd
Quoted 16 lines · Lorenzo Pieralisi
arch/arm64/kernel/cpuidle.c new file +10 -0
new file mode 100644
index 0000000..46eb3ea
@@ -0,0 +1,27 @@
+/*
+ * ARM64 CPU idle arch support
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ * Author: Lorenzo Pieralisi <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
Quoted 8 lines

arch/arm64/kernel/cpuidle.c line 16

Quoted 6 lines
arch/arm64/kernel/cpuidle.c new file +6 -0
+
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <asm/cpu_ops.h>
+
+int cpu_init_idle(unsigned int cpu)

sparse will probably complain here unless we include <asm/cpuidle.h> right?

Yes, I missed that, now fixed, thanks a lot.

Lorenzo

#14 lore raw

PATCH v6 4/7 arm64: add PSCI CPU_SUSPEND based cpu_suspend support

In reply to Lorenzo Pieralisi

This patch implements the cpu_suspend cpu operations method through the PSCI CPU SUSPEND API. The PSCI implementation translates the idle state index passed by the cpu_suspend core call into a valid PSCI state according to the PSCI states initialized at boot through the cpu_init_idle() CPU operations hook.

Entry point is set to cpu_resume physical address, that represents the default kernel execution address following a CPU reset; for standby states the entry point address is useless and will therefore be ignored by the PSCI implementation.

---
 arch/arm64/kernel/psci.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 89 insertions(+)
arch/arm64/kernel/psci.c +89 -0
index a623c44..bbdf41d 100644
@@ -21,6 +21,7 @@
 #include <linux/reboot.h>
 #include <linux/pm.h>
 #include <linux/delay.h>
+#include <linux/slab.h>
 #include <uapi/linux/psci.h>
 
 #include <asm/compiler.h>
@@ -28,6 +29,7 @@
 #include <asm/errno.h>
 #include <asm/psci.h>
 #include <asm/smp_plat.h>
+#include <asm/suspend.h>
 #include <asm/system_misc.h>
 
 #define PSCI_POWER_STATE_TYPE_STANDBY		0
@@ -65,6 +67,8 @@ enum psci_function {
 	PSCI_FN_MAX,
 };
 
+static DEFINE_PER_CPU_READ_MOSTLY(struct psci_power_state *, psci_power_state);
+
 static u32 psci_function_id[PSCI_FN_MAX];
 
 static int psci_to_linux_errno(int errno)
@@ -93,6 +97,18 @@ static u32 psci_power_state_pack(struct psci_power_state state)
 		 & PSCI_0_2_POWER_STATE_AFFL_MASK);
 }
 
+static void psci_power_state_unpack(u32 power_state,
+				    struct psci_power_state *state)
+{
+	state->id = (power_state & PSCI_0_2_POWER_STATE_ID_MASK) >>
+			PSCI_0_2_POWER_STATE_ID_SHIFT;
+	state->type = (power_state & PSCI_0_2_POWER_STATE_TYPE_MASK) >>
+			PSCI_0_2_POWER_STATE_TYPE_SHIFT;
+	state->affinity_level =
+			(power_state & PSCI_0_2_POWER_STATE_AFFL_MASK) >>
+			PSCI_0_2_POWER_STATE_AFFL_SHIFT;
+}
+
 /*
  * The following two functions are invoked via the invoke_psci_fn pointer
  * and will not be inlined, allowing us to piggyback on the AAPCS.
@@ -199,6 +215,61 @@ static int psci_migrate_info_type(void)
 	return err;
 }
 
+static int cpu_psci_cpu_init_idle(struct device_node *cpu_node,
+				  unsigned int cpu)
+{
+	int i, ret, count = 0;
+	struct psci_power_state *psci_states;
+	struct device_node *state_node;
+
+	/*
+	 * If the PSCI cpu_suspend function hook has not been initialized
+	 * idle states must not be enabled, so bail out
+	 */
+	if (!psci_ops.cpu_suspend)
+		return -EOPNOTSUPP;
+
+	/* Count idle states */
+	while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
+					      count)))
+		count++;
+
+	if (!count)
+		return -ENODEV;
+
+	psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL);
+	if (!psci_states) {
+		pr_warn("idle state psci_state allocation failed\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < count; i++) {
+		u32 psci_power_state;
+
+		state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
+
+		ret = of_property_read_u32(state_node,
+					   "arm,psci-suspend-param",
+					   &psci_power_state);
+		if (ret) {
+			pr_warn(" * %s missing arm,psci-suspend-param property\n",
+				state_node->full_name);
+			goto free_mem;
+		}
+
+		pr_debug("psci-power-state %#x index %d\n", psci_power_state,
+							    i);
+		psci_power_state_unpack(psci_power_state, &psci_states[i]);
+	}
+	/* Idle states parsed correctly, initialize per-cpu pointer */
+	per_cpu(psci_power_state, cpu) = psci_states;
+	return 0;
+
+free_mem:
+	kfree(psci_states);
+	return ret;
+}
+
 static int get_set_conduit_method(struct device_node *np)
 {
 	const char *method;
@@ -436,8 +507,23 @@ static int cpu_psci_cpu_kill(unsigned int cpu)
 #endif
 #endif
 
+static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index)
+{
+	struct psci_power_state *state = __get_cpu_var(psci_power_state);
+	/*
+	 * idle state index 0 corresponds to wfi, should never be called
+	 * from the cpu_suspend operations
+	 */
+	if (WARN_ON_ONCE(!index))
+		return -EINVAL;
+
+	return psci_ops.cpu_suspend(state[index - 1],
+				    virt_to_phys(cpu_resume));
+}
+
 const struct cpu_operations cpu_psci_ops = {
 	.name		= "psci",
+	.cpu_init_idle	= cpu_psci_cpu_init_idle,
 #ifdef CONFIG_SMP
 	.cpu_init	= cpu_psci_cpu_init,
 	.cpu_prepare	= cpu_psci_cpu_prepare,
@@ -448,5 +534,8 @@ const struct cpu_operations cpu_psci_ops = {
 	.cpu_kill	= cpu_psci_cpu_kill,
 #endif
 #endif
+#ifdef CONFIG_ARM64_CPU_SUSPEND
+	.cpu_suspend	= cpu_psci_cpu_suspend,
+#endif
 };
 
Signature · 1 line
1.9.1
#15 lore raw

PATCH v6 4/7 arm64: add PSCI CPU_SUSPEND based cpu_suspend support

Quoted 160 lines · Lorenzo Pieralisi

This patch implements the cpu_suspend cpu operations method through the PSCI CPU SUSPEND API. The PSCI implementation translates the idle state index passed by the cpu_suspend core call into a valid PSCI state according to the PSCI states initialized at boot through the cpu_init_idle() CPU operations hook.

Entry point is set to cpu_resume physical address, that represents the default kernel execution address following a CPU reset; for standby states the entry point address is useless and will therefore be ignored by the PSCI implementation.

---
 arch/arm64/kernel/psci.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 89 insertions(+)
arch/arm64/kernel/psci.c +89 -1
index a623c44..bbdf41d 100644
@@ -21,6 +21,7 @@
 #include <linux/reboot.h>
 #include <linux/pm.h>
 #include <linux/delay.h>
+#include <linux/slab.h>
 #include <uapi/linux/psci.h>
 
 #include <asm/compiler.h>
@@ -28,6 +29,7 @@
 #include <asm/errno.h>
 #include <asm/psci.h>
 #include <asm/smp_plat.h>
+#include <asm/suspend.h>
 #include <asm/system_misc.h>
 
 #define PSCI_POWER_STATE_TYPE_STANDBY          0
@@ -65,6 +67,8 @@ enum psci_function {
        PSCI_FN_MAX,
 };
 
+static DEFINE_PER_CPU_READ_MOSTLY(struct psci_power_state *, psci_power_state);
+
 static u32 psci_function_id[PSCI_FN_MAX];
 
 static int psci_to_linux_errno(int errno)
@@ -93,6 +97,18 @@ static u32 psci_power_state_pack(struct psci_power_state state)
                 & PSCI_0_2_POWER_STATE_AFFL_MASK);
 }
 
+static void psci_power_state_unpack(u32 power_state,
+                                   struct psci_power_state *state)
+{
+       state->id = (power_state & PSCI_0_2_POWER_STATE_ID_MASK) >>
+                       PSCI_0_2_POWER_STATE_ID_SHIFT;
+       state->type = (power_state & PSCI_0_2_POWER_STATE_TYPE_MASK) >>
+                       PSCI_0_2_POWER_STATE_TYPE_SHIFT;
+       state->affinity_level =
+                       (power_state & PSCI_0_2_POWER_STATE_AFFL_MASK) >>
+                       PSCI_0_2_POWER_STATE_AFFL_SHIFT;
+}
+
 /*
  * The following two functions are invoked via the invoke_psci_fn pointer
  * and will not be inlined, allowing us to piggyback on the AAPCS.
@@ -199,6 +215,61 @@ static int psci_migrate_info_type(void)
        return err;
 }
 
+static int cpu_psci_cpu_init_idle(struct device_node *cpu_node,
+                                 unsigned int cpu)
+{
+       int i, ret, count = 0;
+       struct psci_power_state *psci_states;
+       struct device_node *state_node;
+
+       /*
+        * If the PSCI cpu_suspend function hook has not been initialized
+        * idle states must not be enabled, so bail out
+        */
+       if (!psci_ops.cpu_suspend)
+               return -EOPNOTSUPP;
+
+       /* Count idle states */
+       while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
+                                             count)))
+               count++;
+
+       if (!count)
+               return -ENODEV;
+
+       psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL);
+       if (!psci_states) {
+               pr_warn("idle state psci_state allocation failed\n");
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < count; i++) {
+               u32 psci_power_state;
+
+               state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
+
+               ret = of_property_read_u32(state_node,
+                                          "arm,psci-suspend-param",
+                                          &psci_power_state);
+               if (ret) {
+                       pr_warn(" * %s missing arm,psci-suspend-param property\n",
+                               state_node->full_name);
+                       goto free_mem;
+               }
+
+               pr_debug("psci-power-state %#x index %d\n", psci_power_state,
+                                                           i);
+               psci_power_state_unpack(psci_power_state, &psci_states[i]);
+       }
+       /* Idle states parsed correctly, initialize per-cpu pointer */
+       per_cpu(psci_power_state, cpu) = psci_states;
+       return 0;
+
+free_mem:
+       kfree(psci_states);
+       return ret;
+}
+
 static int get_set_conduit_method(struct device_node *np)
 {
        const char *method;
@@ -436,8 +507,23 @@ static int cpu_psci_cpu_kill(unsigned int cpu)
 #endif
 #endif
 
+static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index)
+{
+       struct psci_power_state *state = __get_cpu_var(psci_power_state);
+       /*
+        * idle state index 0 corresponds to wfi, should never be called
+        * from the cpu_suspend operations
+        */
+       if (WARN_ON_ONCE(!index))
+               return -EINVAL;
+
+       return psci_ops.cpu_suspend(state[index - 1],
+                                   virt_to_phys(cpu_resume));
+}
+
 const struct cpu_operations cpu_psci_ops = {
        .name           = "psci",
+       .cpu_init_idle  = cpu_psci_cpu_init_idle,
 #ifdef CONFIG_SMP
        .cpu_init       = cpu_psci_cpu_init,
        .cpu_prepare    = cpu_psci_cpu_prepare,
@@ -448,5 +534,8 @@ const struct cpu_operations cpu_psci_ops = {
        .cpu_kill       = cpu_psci_cpu_kill,
 #endif
 #endif
+#ifdef CONFIG_ARM64_CPU_SUSPEND
+       .cpu_suspend    = cpu_psci_cpu_suspend,
+#endif
 };
 
--
Quoted 1 line

1.9.1

Quoted 7 lines

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

#16 lore raw

PATCH v6 5/7 drivers: cpuidle: CPU idle ARM64 driver

In reply to Lorenzo Pieralisi

This patch implements a generic CPU idle driver for ARM64 machines.

It relies on the DT idle states infrastructure to initialize idle states count and respective parameters. Current code assumes the driver is managing idle states on all possible CPUs but can be easily generalized to support heterogenous systems and build cpumasks at runtime using MIDRs or DT cpu nodes compatible properties.

The driver relies on the arm64 CPU operations to call the idle initialization hook used to parse and save suspend back-end specific idle states information upon probing.

Idle state index 0 is always initialized as a simple wfi state, ie always considered present and functional on all ARM64 platforms.

Idle state indices higher than 0 trigger idle state entry by calling the cpu_suspend function, that does save the CPU context and executes the CPU operations suspend back-end hook. cpu_suspend passes the idle state index as a parameter so that the CPU operations suspend back-end can retrieve the required idle state data by using the idle state index to execute a look-up on its internal data structures.

---
 drivers/cpuidle/Kconfig         |   5 ++
 drivers/cpuidle/Kconfig.arm64   |  14 +++++
 drivers/cpuidle/Makefile        |   4 ++
 drivers/cpuidle/cpuidle-arm64.c | 128 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 151 insertions(+)
 create mode 100644 drivers/cpuidle/Kconfig.arm64
 create mode 100644 drivers/cpuidle/cpuidle-arm64.c
drivers/cpuidle/Kconfig +5 -0
index 414e7a96..ebf0c2e 100644
@@ -43,6 +43,11 @@ depends on ARM
 source "drivers/cpuidle/Kconfig.arm"
 endmenu
 
+menu "ARM64 CPU Idle Drivers"
+depends on ARM64
+source "drivers/cpuidle/Kconfig.arm64"
+endmenu
+
 menu "MIPS CPU Idle Drivers"
 depends on MIPS
 source "drivers/cpuidle/Kconfig.mips"
drivers/cpuidle/Kconfig.arm64 new file +14 -0
new file mode 100644
index 0000000..d0a08ed
@@ -0,0 +1,14 @@
+#
+# ARM64 CPU Idle drivers
+#
+
+config ARM64_CPUIDLE
+	bool "Generic ARM64 CPU idle Driver"
+	select ARM64_CPU_SUSPEND
+	select DT_IDLE_STATES
+	help
+	  Select this to enable generic cpuidle driver for ARM64.
+	  It provides a generic idle driver whose idle states are configured
+	  at run-time through DT nodes. The CPUidle suspend backend is
+	  initialized by calling the CPU operations init idle hook
+	  provided by architecture code.
drivers/cpuidle/Makefile +4 -0
index b27a062..706cb7f 100644
@@ -23,6 +23,10 @@ obj-$(CONFIG_ARM_EXYNOS_CPUIDLE)        += cpuidle-exynos.o
 obj-$(CONFIG_MIPS_CPS_CPUIDLE)		+= cpuidle-cps.o
 
 ###############################################################################
+# ARM64 drivers
+obj-$(CONFIG_ARM64_CPUIDLE)		+= cpuidle-arm64.o
+
+###############################################################################
 # POWERPC drivers
 obj-$(CONFIG_PSERIES_CPUIDLE)		+= cpuidle-pseries.o
 obj-$(CONFIG_POWERNV_CPUIDLE)		+= cpuidle-powernv.o
drivers/cpuidle/cpuidle-arm64.c new file +128 -0

128 lines not on this page · raw message

Signature · 1 line
1.9.1
#17 lore raw

PATCH v6 5/7 drivers: cpuidle: CPU idle ARM64 driver

Quoted 218 lines · Lorenzo Pieralisi

This patch implements a generic CPU idle driver for ARM64 machines.

It relies on the DT idle states infrastructure to initialize idle states count and respective parameters. Current code assumes the driver is managing idle states on all possible CPUs but can be easily generalized to support heterogenous systems and build cpumasks at runtime using MIDRs or DT cpu nodes compatible properties.

The driver relies on the arm64 CPU operations to call the idle initialization hook used to parse and save suspend back-end specific idle states information upon probing.

Idle state index 0 is always initialized as a simple wfi state, ie always considered present and functional on all ARM64 platforms.

Idle state indices higher than 0 trigger idle state entry by calling the cpu_suspend function, that does save the CPU context and executes the CPU operations suspend back-end hook. cpu_suspend passes the idle state index as a parameter so that the CPU operations suspend back-end can retrieve the required idle state data by using the idle state index to execute a look-up on its internal data structures.

---
 drivers/cpuidle/Kconfig         |   5 ++
 drivers/cpuidle/Kconfig.arm64   |  14 +++++
 drivers/cpuidle/Makefile        |   4 ++
 drivers/cpuidle/cpuidle-arm64.c | 128 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 151 insertions(+)
 create mode 100644 drivers/cpuidle/Kconfig.arm64
 create mode 100644 drivers/cpuidle/cpuidle-arm64.c
drivers/cpuidle/Kconfig +5 -0
index 414e7a96..ebf0c2e 100644
@@ -43,6 +43,11 @@ depends on ARM
 source "drivers/cpuidle/Kconfig.arm"
 endmenu
 
+menu "ARM64 CPU Idle Drivers"
+depends on ARM64
+source "drivers/cpuidle/Kconfig.arm64"
+endmenu
+
 menu "MIPS CPU Idle Drivers"
 depends on MIPS
 source "drivers/cpuidle/Kconfig.mips"
drivers/cpuidle/Kconfig.arm64 new file +14 -0
new file mode 100644
index 0000000..d0a08ed
@@ -0,0 +1,14 @@
+#
+# ARM64 CPU Idle drivers
+#
+
+config ARM64_CPUIDLE
+       bool "Generic ARM64 CPU idle Driver"
+       select ARM64_CPU_SUSPEND
+       select DT_IDLE_STATES
+       help
+         Select this to enable generic cpuidle driver for ARM64.
+         It provides a generic idle driver whose idle states are configured
+         at run-time through DT nodes. The CPUidle suspend backend is
+         initialized by calling the CPU operations init idle hook
+         provided by architecture code.
drivers/cpuidle/Makefile +4 -0
index b27a062..706cb7f 100644
@@ -23,6 +23,10 @@ obj-$(CONFIG_ARM_EXYNOS_CPUIDLE)        += cpuidle-exynos.o
 obj-$(CONFIG_MIPS_CPS_CPUIDLE)         += cpuidle-cps.o
 
 ###############################################################################
+# ARM64 drivers
+obj-$(CONFIG_ARM64_CPUIDLE)            += cpuidle-arm64.o
+
+###############################################################################
 # POWERPC drivers
 obj-$(CONFIG_PSERIES_CPUIDLE)          += cpuidle-pseries.o
 obj-$(CONFIG_POWERNV_CPUIDLE)          += cpuidle-powernv.o
drivers/cpuidle/cpuidle-arm64.c new file +128 -1
new file mode 100644
index 0000000..1aeb426
@@ -0,0 +1,128 @@
+/*
+ * ARM64 generic CPU idle driver.
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ * Author: Lorenzo Pieralisi <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) "CPUidle arm64: " fmt
+
+#include <linux/cpuidle.h>
+#include <linux/cpumask.h>
+#include <linux/cpu_pm.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+#include <asm/cpuidle.h>
+#include <asm/suspend.h>
+
+#include "dt_idle_states.h"
+
+/*
+ * arm_enter_idle_state - Programs CPU to enter the specified state
+ *
+ * dev: cpuidle device
+ * drv: cpuidle driver
+ * idx: state index
+ *
+ * Called from the CPUidle framework to program the device to the
+ * specified target state selected by the governor.
+ */
+static int arm_enter_idle_state(struct cpuidle_device *dev,
+                               struct cpuidle_driver *drv, int idx)
+{
+       int ret;
+
+       if (!idx) {
+               cpu_do_idle();
+               return idx;
+       }
+
+       ret = cpu_pm_enter();
+       if (!ret) {
+               /*
+                * Pass idle state index to cpu_suspend which in turn will
+                * call the CPU ops suspend protocol with idle index as a
+                * parameter.
+                */
+               ret = cpu_suspend(idx);
+
+               cpu_pm_exit();
+       }
+
+       return ret ? -1 : idx;
+}
+
+struct cpuidle_driver arm64_idle_driver = {
+       .name = "arm64_idle",
+       .owner = THIS_MODULE,
+       /*
+        * State at index 0 is standby wfi and considered standard
+        * on all ARM platforms. If in some platforms simple wfi
+        * can't be used as "state 0", DT bindings must be implemented
+        * to work around this issue and allow installing a special
+        * handler for idle state index 0.
+        */
+       .states[0] = {
+               .enter                  = arm_enter_idle_state,
+               .exit_latency           = 1,
+               .target_residency       = 1,
+               .power_usage            = UINT_MAX,
+               .flags                  = CPUIDLE_FLAG_TIME_VALID,
+               .name                   = "WFI",
+               .desc                   = "ARM64 WFI",
+       }
+};
+
+/*
+ * arm64_idle_init
+ *
+ * Registers the arm64 specific cpuidle driver with the cpuidle
+ * framework. It relies on core code to parse the idle states
+ * and initialize them using driver data structures accordingly.
+ */
+static int __init arm64_idle_init(void)
+{
+       int i, ret;
+       struct cpuidle_driver *drv = &arm64_idle_driver;
+
+       drv->cpumask = kzalloc(cpumask_size(), GFP_KERNEL);
+       if (!drv->cpumask)
+               return -ENOMEM;
+
+       cpumask_copy(drv->cpumask, cpu_possible_mask);
+       /*
+        * Start at index 1, request idle state nodes to be filled
+        */
+       ret = dt_init_idle_driver(drv, 1);
+       if (ret)
+               goto free_mem;
+       /*
+        * Call arch CPU operations in order to initialize
+        * idle states suspend back-end specific data
+        */
+       for_each_cpu(i, drv->cpumask) {
+               ret = cpu_init_idle(i);
+               if (ret)
+                       goto free_mem;
+       }
+
+       for (i = 1; i < drv->state_count; i++)
+               drv->states[i].enter = arm_enter_idle_state;
+
+       ret = cpuidle_register(drv, NULL);
+       if (ret)
+               goto free_mem;
+
+       return 0;
+free_mem:
+       kfree(drv->cpumask);
+       return ret;
+}
+device_initcall(arm64_idle_init);
--
Quoted 4 lines

1.9.1

Quoted 4 lines

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

#18 lore raw

PATCH v6 6/7 drivers: cpuidle: initialize big.LITTLE driver through DT

In reply to Lorenzo Pieralisi

With the introduction of DT based idle states, CPUidle drivers for ARM can now initialize idle states data through properties in the device tree.

This patch adds code to the big.LITTLE CPUidle driver to dynamically initialize idle states data through the updated device tree source file.

---
 arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts | 23 ++++++++++++++++
 drivers/cpuidle/Kconfig.arm                |  1 +
 drivers/cpuidle/cpuidle-big_little.c       | 43 +++++++++++++++---------------
 3 files changed, 46 insertions(+), 21 deletions(-)
arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts +23 -0
index a25c262..d61da86 100644
@@ -38,6 +38,7 @@
 			compatible = "arm,cortex-a15";
 			reg = <0>;
 			cci-control-port = <&cci_control1>;
+			cpu-idle-states = <&CLUSTER_SLEEP_BIG>;
 		};
 
 		cpu1: cpu at 1 {
@@ -45,6 +46,7 @@
 			compatible = "arm,cortex-a15";
 			reg = <1>;
 			cci-control-port = <&cci_control1>;
+			cpu-idle-states = <&CLUSTER_SLEEP_BIG>;
 		};
 
 		cpu2: cpu at 2 {
@@ -52,6 +54,7 @@
 			compatible = "arm,cortex-a7";
 			reg = <0x100>;
 			cci-control-port = <&cci_control2>;
+			cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>;
 		};
 
 		cpu3: cpu at 3 {
@@ -59,6 +62,7 @@
 			compatible = "arm,cortex-a7";
 			reg = <0x101>;
 			cci-control-port = <&cci_control2>;
+			cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>;
 		};
 
 		cpu4: cpu at 4 {
@@ -66,6 +70,25 @@
 			compatible = "arm,cortex-a7";
 			reg = <0x102>;
 			cci-control-port = <&cci_control2>;
+			cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>;
+		};
+
+		idle-states {
+			CLUSTER_SLEEP_BIG: cluster-sleep-big {
+				compatible = "arm,idle-state";
+				local-timer-stop;
+				entry-latency-us = <1000>;
+				exit-latency-us = <700>;
+				min-residency-us = <3500>;
+			};
+
+			CLUSTER_SLEEP_LITTLE: cluster-sleep-little {
+				compatible = "arm,idle-state";
+				local-timer-stop;
+				entry-latency-us = <1000>;
+				exit-latency-us = <500>;
+				min-residency-us = <3000>;
+			};
 		};
 	};
 
drivers/cpuidle/Kconfig.arm +1 -0
index b6d69e8..a9b089c 100644
@@ -12,6 +12,7 @@ config ARM_BIG_LITTLE_CPUIDLE
 	depends on ARCH_VEXPRESS_TC2_PM
 	select ARM_CPU_SUSPEND
 	select CPU_IDLE_MULTIPLE_DRIVERS
+	select DT_IDLE_STATES
 	help
 	  Select this option to enable CPU idle driver for big.LITTLE based
 	  ARM systems. Driver manages CPUs coordination through MCPM and
drivers/cpuidle/cpuidle-big_little.c +22 -21

73 lines not on this page · raw message

Signature · 1 line
1.9.1
maintainer
#19 lore raw

PATCH v6 6/7 drivers: cpuidle: initialize big.LITTLE driver through DT

Quoted 8 lines · Lorenzo Pieralisi

With the introduction of DT based idle states, CPUidle drivers for ARM can now initialize idle states data through properties in the device tree.

This patch adds code to the big.LITTLE CPUidle driver to dynamically initialize idle states data through the updated device tree source file.

I think for this patch (and the exynos one) we should still allow the driver to work fine with old DT. Basically if the information is not in DT, fall back to the original static definitions.

Signature · 1 line
Catalin
#20 lore raw

PATCH v6 6/7 drivers: cpuidle: initialize big.LITTLE driver through DT

Quoted 7 lines · Catalin Marinas
Quoted 6 lines · Lorenzo Pieralisi

With the introduction of DT based idle states, CPUidle drivers for ARM can now initialize idle states data through properties in the device tree.

This patch adds code to the big.LITTLE CPUidle driver to dynamically initialize idle states data through the updated device tree source file.

Quoted 6 lines
Quoted 2 lines

I think for this patch (and the exynos one) we should still allow the driver to work fine with old DT. Basically if the information is not in DT, fall back to the original static definitions.

Yes, this makes sense, I will implement the fall-back mechanism on v7, that should be final. If in the meanwhile Samsung guys have time to test the Exynos patch that would be very appreciated.

Thanks,
Lorenzo

#21 lore raw

PATCH v6 7/7 drivers: cpuidle: initialize Exynos driver through DT

In reply to Lorenzo Pieralisi

With the introduction of DT based idle states, CPUidle drivers for ARM can now initialize idle states data through properties in the device tree.

This patch adds code to the Exynos CPUidle driver to dynamically initialize idle states data through the updated device tree source files.

Cc Chander Kashyap <[email protected]>
Cc Kukjin Kim <[email protected]>
Cc Tomasz Figa <[email protected]>
---
 arch/arm/boot/dts/exynos4210.dtsi | 11 +++++++++++
 arch/arm/boot/dts/exynos5250.dtsi | 11 +++++++++++
 drivers/cpuidle/Kconfig.arm       |  1 +
 drivers/cpuidle/cpuidle-exynos.c  | 37 ++++++++++++++++++++++++-------------
 4 files changed, 47 insertions(+), 13 deletions(-)
arch/arm/boot/dts/exynos4210.dtsi +11 -0
index bc2b444..0270100 100644
@@ -39,12 +39,23 @@
 			device_type = "cpu";
 			compatible = "arm,cortex-a9";
 			reg = <0x900>;
+			cpu-idle-states = <&CLUSTER_SLEEP_0>;
 		};
 
 		cpu at 901 {
 			device_type = "cpu";
 			compatible = "arm,cortex-a9";
 			reg = <0x901>;
+			cpu-idle-states = <&CLUSTER_SLEEP_0>;
+		};
+
+		idle-states {
+			CLUSTER_SLEEP_0: cluster-sleep-0 {
+				compatible = "arm,idle-state";
+				entry-latency-us = <1000>;
+				exit-latency-us = <300>;
+				min-residency-us = <100000>;
+			};
 		};
 	};
 
arch/arm/boot/dts/exynos5250.dtsi +11 -0
index 834fb5a..631906e 100644
@@ -63,12 +63,23 @@
 			compatible = "arm,cortex-a15";
 			reg = <0>;
 			clock-frequency = <1700000000>;
+			cpu-idle-states = <&CLUSTER_SLEEP_0>;
 		};
 		cpu at 1 {
 			device_type = "cpu";
 			compatible = "arm,cortex-a15";
 			reg = <1>;
 			clock-frequency = <1700000000>;
+			cpu-idle-states = <&CLUSTER_SLEEP_0>;
+		};
+
+		idle-states {
+			CLUSTER_SLEEP_0: cluster-sleep-0 {
+				compatible = "arm,idle-state";
+				entry-latency-us = <1000>;
+				exit-latency-us = <300>;
+				min-residency-us = <100000>;
+			};
 		};
 	};
 
drivers/cpuidle/Kconfig.arm +1 -0
index a9b089c..d8a9cd2 100644
@@ -60,5 +60,6 @@ config ARM_AT91_CPUIDLE
 config ARM_EXYNOS_CPUIDLE
 	bool "Cpu Idle Driver for the Exynos processors"
 	depends on ARCH_EXYNOS
+	select DT_IDLE_STATES
 	help
 	  Select this to enable cpuidle for Exynos processors
drivers/cpuidle/cpuidle-exynos.c +24 -13
index 7c01512..097c21d 100644
@@ -13,11 +13,14 @@
 #include <linux/export.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/slab.h>
 
 #include <asm/proc-fns.h>
 #include <asm/suspend.h>
 #include <asm/cpuidle.h>
 
+#include "dt_idle_states.h"
+
 static void (*exynos_enter_aftr)(void);
 
 static int idle_finisher(unsigned long flags)
@@ -60,32 +63,40 @@ static struct cpuidle_driver exynos_idle_driver = {
 	.owner			= THIS_MODULE,
 	.states = {
 		[0] = ARM_CPUIDLE_WFI_STATE,
-		[1] = {
-			.enter			= exynos_enter_lowpower,
-			.exit_latency		= 300,
-			.target_residency	= 100000,
-			.flags			= CPUIDLE_FLAG_TIME_VALID,
-			.name			= "C1",
-			.desc			= "ARM power down",
-		},
 	},
-	.state_count = 2,
-	.safe_state_index = 0,
 };
 
 static int exynos_cpuidle_probe(struct platform_device *pdev)
 {
-	int ret;
+	int ret, i;
+	struct cpuidle_driver *drv = &exynos_idle_driver;
 
 	exynos_enter_aftr = (void *)(pdev->dev.platform_data);
 
-	ret = cpuidle_register(&exynos_idle_driver, NULL);
+	drv->cpumask = kzalloc(cpumask_size(), GFP_KERNEL);
+	if (!drv->cpumask)
+		return -ENOMEM;
+
+	/* Start at index 1, index 0 standard WFI */
+	ret = dt_init_idle_driver(drv, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to initialize idle states\n");
+		goto free_mem;
+	}
+
+	for (i = 1; i < drv->state_count; i++)
+		drv->states[i].enter = exynos_enter_lowpower;
+
+	ret = cpuidle_register(drv, NULL);
 	if (ret) {
 		dev_err(&pdev->dev, "failed to register cpuidle driver\n");
-		return ret;
+		goto free_mem;
 	}
 
 	return 0;
+free_mem:
+	kfree(drv->cpumask);
+	return ret;
 }
 
 static struct platform_driver exynos_cpuidle_driver = {
Signature · 1 line
1.9.1