Merge branch 'pm-opp'
* pm-opp: PM / OPP: Use snprintf() instead of sprintf() PM / OPP: Set cpu_dev->id in cpumask first PM / OPP: Fix parsing of opp-microvolt and opp-microamp properties PM / OPP: Parse 'opp-<prop>-<name>' bindings PM / OPP: Parse 'opp-supported-hw' binding PM / OPP: Add missing doc comments ARM: dts: exynos4412: Rename OPP nodes as opp@<opp-hz> PM / OPP: Rename OPP nodes as opp@<opp-hz> PM / OPP: Remove 'operating-points-names' binding PM / OPP: Add {opp-microvolt|opp-microamp}-<name> binding PM / OPP: Add "opp-supported-hw" binding PM / OPP: Add debugfs support
This commit is contained in:
commit
7f4a3702bd
|
@ -45,21 +45,10 @@ Devices supporting OPPs must set their "operating-points-v2" property with
|
||||||
phandle to a OPP table in their DT node. The OPP core will use this phandle to
|
phandle to a OPP table in their DT node. The OPP core will use this phandle to
|
||||||
find the operating points for the device.
|
find the operating points for the device.
|
||||||
|
|
||||||
Devices may want to choose OPP tables at runtime and so can provide a list of
|
|
||||||
phandles here. But only *one* of them should be chosen at runtime. This must be
|
|
||||||
accompanied by a corresponding "operating-points-names" property, to uniquely
|
|
||||||
identify the OPP tables.
|
|
||||||
|
|
||||||
If required, this can be extended for SoC vendor specfic bindings. Such bindings
|
If required, this can be extended for SoC vendor specfic bindings. Such bindings
|
||||||
should be documented as Documentation/devicetree/bindings/power/<vendor>-opp.txt
|
should be documented as Documentation/devicetree/bindings/power/<vendor>-opp.txt
|
||||||
and should have a compatible description like: "operating-points-v2-<vendor>".
|
and should have a compatible description like: "operating-points-v2-<vendor>".
|
||||||
|
|
||||||
Optional properties:
|
|
||||||
- operating-points-names: Names of OPP tables (required if multiple OPP
|
|
||||||
tables are present), to uniquely identify them. The same list must be present
|
|
||||||
for all the CPUs which are sharing clock/voltage rails and hence the OPP
|
|
||||||
tables.
|
|
||||||
|
|
||||||
* OPP Table Node
|
* OPP Table Node
|
||||||
|
|
||||||
This describes the OPPs belonging to a device. This node can have following
|
This describes the OPPs belonging to a device. This node can have following
|
||||||
|
@ -100,6 +89,14 @@ Optional properties:
|
||||||
Entries for multiple regulators must be present in the same order as
|
Entries for multiple regulators must be present in the same order as
|
||||||
regulators are specified in device's DT node.
|
regulators are specified in device's DT node.
|
||||||
|
|
||||||
|
- opp-microvolt-<name>: Named opp-microvolt property. This is exactly similar to
|
||||||
|
the above opp-microvolt property, but allows multiple voltage ranges to be
|
||||||
|
provided for the same OPP. At runtime, the platform can pick a <name> and
|
||||||
|
matching opp-microvolt-<name> property will be enabled for all OPPs. If the
|
||||||
|
platform doesn't pick a specific <name> or the <name> doesn't match with any
|
||||||
|
opp-microvolt-<name> properties, then opp-microvolt property shall be used, if
|
||||||
|
present.
|
||||||
|
|
||||||
- opp-microamp: The maximum current drawn by the device in microamperes
|
- opp-microamp: The maximum current drawn by the device in microamperes
|
||||||
considering system specific parameters (such as transients, process, aging,
|
considering system specific parameters (such as transients, process, aging,
|
||||||
maximum operating temperature range etc.) as necessary. This may be used to
|
maximum operating temperature range etc.) as necessary. This may be used to
|
||||||
|
@ -112,6 +109,9 @@ Optional properties:
|
||||||
for few regulators, then this should be marked as zero for them. If it isn't
|
for few regulators, then this should be marked as zero for them. If it isn't
|
||||||
required for any regulator, then this property need not be present.
|
required for any regulator, then this property need not be present.
|
||||||
|
|
||||||
|
- opp-microamp-<name>: Named opp-microamp property. Similar to
|
||||||
|
opp-microvolt-<name> property, but for microamp instead.
|
||||||
|
|
||||||
- clock-latency-ns: Specifies the maximum possible transition latency (in
|
- clock-latency-ns: Specifies the maximum possible transition latency (in
|
||||||
nanoseconds) for switching to this OPP from any other OPP.
|
nanoseconds) for switching to this OPP from any other OPP.
|
||||||
|
|
||||||
|
@ -123,6 +123,26 @@ Optional properties:
|
||||||
- opp-suspend: Marks the OPP to be used during device suspend. Only one OPP in
|
- opp-suspend: Marks the OPP to be used during device suspend. Only one OPP in
|
||||||
the table should have this.
|
the table should have this.
|
||||||
|
|
||||||
|
- opp-supported-hw: This enables us to select only a subset of OPPs from the
|
||||||
|
larger OPP table, based on what version of the hardware we are running on. We
|
||||||
|
still can't have multiple nodes with the same opp-hz value in OPP table.
|
||||||
|
|
||||||
|
It's an user defined array containing a hierarchy of hardware version numbers,
|
||||||
|
supported by the OPP. For example: a platform with hierarchy of three levels
|
||||||
|
of versions (A, B and C), this field should be like <X Y Z>, where X
|
||||||
|
corresponds to Version hierarchy A, Y corresponds to version hierarchy B and Z
|
||||||
|
corresponds to version hierarchy C.
|
||||||
|
|
||||||
|
Each level of hierarchy is represented by a 32 bit value, and so there can be
|
||||||
|
only 32 different supported version per hierarchy. i.e. 1 bit per version. A
|
||||||
|
value of 0xFFFFFFFF will enable the OPP for all versions for that hierarchy
|
||||||
|
level. And a value of 0x00000000 will disable the OPP completely, and so we
|
||||||
|
never want that to happen.
|
||||||
|
|
||||||
|
If 32 values aren't sufficient for a version hierarchy, than that version
|
||||||
|
hierarchy can be contained in multiple 32 bit values. i.e. <X Y Z1 Z2> in the
|
||||||
|
above example, Z1 & Z2 refer to the version hierarchy Z.
|
||||||
|
|
||||||
- status: Marks the node enabled/disabled.
|
- status: Marks the node enabled/disabled.
|
||||||
|
|
||||||
Example 1: Single cluster Dual-core ARM cortex A9, switch DVFS states together.
|
Example 1: Single cluster Dual-core ARM cortex A9, switch DVFS states together.
|
||||||
|
@ -157,20 +177,20 @@ Example 1: Single cluster Dual-core ARM cortex A9, switch DVFS states together.
|
||||||
compatible = "operating-points-v2";
|
compatible = "operating-points-v2";
|
||||||
opp-shared;
|
opp-shared;
|
||||||
|
|
||||||
opp00 {
|
opp@1000000000 {
|
||||||
opp-hz = /bits/ 64 <1000000000>;
|
opp-hz = /bits/ 64 <1000000000>;
|
||||||
opp-microvolt = <970000 975000 985000>;
|
opp-microvolt = <970000 975000 985000>;
|
||||||
opp-microamp = <70000>;
|
opp-microamp = <70000>;
|
||||||
clock-latency-ns = <300000>;
|
clock-latency-ns = <300000>;
|
||||||
opp-suspend;
|
opp-suspend;
|
||||||
};
|
};
|
||||||
opp01 {
|
opp@1100000000 {
|
||||||
opp-hz = /bits/ 64 <1100000000>;
|
opp-hz = /bits/ 64 <1100000000>;
|
||||||
opp-microvolt = <980000 1000000 1010000>;
|
opp-microvolt = <980000 1000000 1010000>;
|
||||||
opp-microamp = <80000>;
|
opp-microamp = <80000>;
|
||||||
clock-latency-ns = <310000>;
|
clock-latency-ns = <310000>;
|
||||||
};
|
};
|
||||||
opp02 {
|
opp@1200000000 {
|
||||||
opp-hz = /bits/ 64 <1200000000>;
|
opp-hz = /bits/ 64 <1200000000>;
|
||||||
opp-microvolt = <1025000>;
|
opp-microvolt = <1025000>;
|
||||||
clock-latency-ns = <290000>;
|
clock-latency-ns = <290000>;
|
||||||
|
@ -236,20 +256,20 @@ independently.
|
||||||
* independently.
|
* independently.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
opp00 {
|
opp@1000000000 {
|
||||||
opp-hz = /bits/ 64 <1000000000>;
|
opp-hz = /bits/ 64 <1000000000>;
|
||||||
opp-microvolt = <970000 975000 985000>;
|
opp-microvolt = <970000 975000 985000>;
|
||||||
opp-microamp = <70000>;
|
opp-microamp = <70000>;
|
||||||
clock-latency-ns = <300000>;
|
clock-latency-ns = <300000>;
|
||||||
opp-suspend;
|
opp-suspend;
|
||||||
};
|
};
|
||||||
opp01 {
|
opp@1100000000 {
|
||||||
opp-hz = /bits/ 64 <1100000000>;
|
opp-hz = /bits/ 64 <1100000000>;
|
||||||
opp-microvolt = <980000 1000000 1010000>;
|
opp-microvolt = <980000 1000000 1010000>;
|
||||||
opp-microamp = <80000>;
|
opp-microamp = <80000>;
|
||||||
clock-latency-ns = <310000>;
|
clock-latency-ns = <310000>;
|
||||||
};
|
};
|
||||||
opp02 {
|
opp@1200000000 {
|
||||||
opp-hz = /bits/ 64 <1200000000>;
|
opp-hz = /bits/ 64 <1200000000>;
|
||||||
opp-microvolt = <1025000>;
|
opp-microvolt = <1025000>;
|
||||||
opp-microamp = <90000;
|
opp-microamp = <90000;
|
||||||
|
@ -312,20 +332,20 @@ DVFS state together.
|
||||||
compatible = "operating-points-v2";
|
compatible = "operating-points-v2";
|
||||||
opp-shared;
|
opp-shared;
|
||||||
|
|
||||||
opp00 {
|
opp@1000000000 {
|
||||||
opp-hz = /bits/ 64 <1000000000>;
|
opp-hz = /bits/ 64 <1000000000>;
|
||||||
opp-microvolt = <970000 975000 985000>;
|
opp-microvolt = <970000 975000 985000>;
|
||||||
opp-microamp = <70000>;
|
opp-microamp = <70000>;
|
||||||
clock-latency-ns = <300000>;
|
clock-latency-ns = <300000>;
|
||||||
opp-suspend;
|
opp-suspend;
|
||||||
};
|
};
|
||||||
opp01 {
|
opp@1100000000 {
|
||||||
opp-hz = /bits/ 64 <1100000000>;
|
opp-hz = /bits/ 64 <1100000000>;
|
||||||
opp-microvolt = <980000 1000000 1010000>;
|
opp-microvolt = <980000 1000000 1010000>;
|
||||||
opp-microamp = <80000>;
|
opp-microamp = <80000>;
|
||||||
clock-latency-ns = <310000>;
|
clock-latency-ns = <310000>;
|
||||||
};
|
};
|
||||||
opp02 {
|
opp@1200000000 {
|
||||||
opp-hz = /bits/ 64 <1200000000>;
|
opp-hz = /bits/ 64 <1200000000>;
|
||||||
opp-microvolt = <1025000>;
|
opp-microvolt = <1025000>;
|
||||||
opp-microamp = <90000>;
|
opp-microamp = <90000>;
|
||||||
|
@ -338,20 +358,20 @@ DVFS state together.
|
||||||
compatible = "operating-points-v2";
|
compatible = "operating-points-v2";
|
||||||
opp-shared;
|
opp-shared;
|
||||||
|
|
||||||
opp10 {
|
opp@1300000000 {
|
||||||
opp-hz = /bits/ 64 <1300000000>;
|
opp-hz = /bits/ 64 <1300000000>;
|
||||||
opp-microvolt = <1045000 1050000 1055000>;
|
opp-microvolt = <1045000 1050000 1055000>;
|
||||||
opp-microamp = <95000>;
|
opp-microamp = <95000>;
|
||||||
clock-latency-ns = <400000>;
|
clock-latency-ns = <400000>;
|
||||||
opp-suspend;
|
opp-suspend;
|
||||||
};
|
};
|
||||||
opp11 {
|
opp@1400000000 {
|
||||||
opp-hz = /bits/ 64 <1400000000>;
|
opp-hz = /bits/ 64 <1400000000>;
|
||||||
opp-microvolt = <1075000>;
|
opp-microvolt = <1075000>;
|
||||||
opp-microamp = <100000>;
|
opp-microamp = <100000>;
|
||||||
clock-latency-ns = <400000>;
|
clock-latency-ns = <400000>;
|
||||||
};
|
};
|
||||||
opp12 {
|
opp@1500000000 {
|
||||||
opp-hz = /bits/ 64 <1500000000>;
|
opp-hz = /bits/ 64 <1500000000>;
|
||||||
opp-microvolt = <1010000 1100000 1110000>;
|
opp-microvolt = <1010000 1100000 1110000>;
|
||||||
opp-microamp = <95000>;
|
opp-microamp = <95000>;
|
||||||
|
@ -378,7 +398,7 @@ Example 4: Handling multiple regulators
|
||||||
compatible = "operating-points-v2";
|
compatible = "operating-points-v2";
|
||||||
opp-shared;
|
opp-shared;
|
||||||
|
|
||||||
opp00 {
|
opp@1000000000 {
|
||||||
opp-hz = /bits/ 64 <1000000000>;
|
opp-hz = /bits/ 64 <1000000000>;
|
||||||
opp-microvolt = <970000>, /* Supply 0 */
|
opp-microvolt = <970000>, /* Supply 0 */
|
||||||
<960000>, /* Supply 1 */
|
<960000>, /* Supply 1 */
|
||||||
|
@ -391,7 +411,7 @@ Example 4: Handling multiple regulators
|
||||||
|
|
||||||
/* OR */
|
/* OR */
|
||||||
|
|
||||||
opp00 {
|
opp@1000000000 {
|
||||||
opp-hz = /bits/ 64 <1000000000>;
|
opp-hz = /bits/ 64 <1000000000>;
|
||||||
opp-microvolt = <970000 975000 985000>, /* Supply 0 */
|
opp-microvolt = <970000 975000 985000>, /* Supply 0 */
|
||||||
<960000 965000 975000>, /* Supply 1 */
|
<960000 965000 975000>, /* Supply 1 */
|
||||||
|
@ -404,7 +424,7 @@ Example 4: Handling multiple regulators
|
||||||
|
|
||||||
/* OR */
|
/* OR */
|
||||||
|
|
||||||
opp00 {
|
opp@1000000000 {
|
||||||
opp-hz = /bits/ 64 <1000000000>;
|
opp-hz = /bits/ 64 <1000000000>;
|
||||||
opp-microvolt = <970000 975000 985000>, /* Supply 0 */
|
opp-microvolt = <970000 975000 985000>, /* Supply 0 */
|
||||||
<960000 965000 975000>, /* Supply 1 */
|
<960000 965000 975000>, /* Supply 1 */
|
||||||
|
@ -417,7 +437,8 @@ Example 4: Handling multiple regulators
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
Example 5: Multiple OPP tables
|
Example 5: opp-supported-hw
|
||||||
|
(example: three level hierarchy of versions: cuts, substrate and process)
|
||||||
|
|
||||||
/ {
|
/ {
|
||||||
cpus {
|
cpus {
|
||||||
|
@ -426,40 +447,73 @@ Example 5: Multiple OPP tables
|
||||||
...
|
...
|
||||||
|
|
||||||
cpu-supply = <&cpu_supply>
|
cpu-supply = <&cpu_supply>
|
||||||
operating-points-v2 = <&cpu0_opp_table_slow>, <&cpu0_opp_table_fast>;
|
operating-points-v2 = <&cpu0_opp_table_slow>;
|
||||||
operating-points-names = "slow", "fast";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
cpu0_opp_table_slow: opp_table_slow {
|
opp_table {
|
||||||
compatible = "operating-points-v2";
|
compatible = "operating-points-v2";
|
||||||
status = "okay";
|
status = "okay";
|
||||||
opp-shared;
|
opp-shared;
|
||||||
|
|
||||||
opp00 {
|
opp@600000000 {
|
||||||
|
/*
|
||||||
|
* Supports all substrate and process versions for 0xF
|
||||||
|
* cuts, i.e. only first four cuts.
|
||||||
|
*/
|
||||||
|
opp-supported-hw = <0xF 0xFFFFFFFF 0xFFFFFFFF>
|
||||||
opp-hz = /bits/ 64 <600000000>;
|
opp-hz = /bits/ 64 <600000000>;
|
||||||
|
opp-microvolt = <900000 915000 925000>;
|
||||||
...
|
...
|
||||||
};
|
};
|
||||||
|
|
||||||
opp01 {
|
opp@800000000 {
|
||||||
|
/*
|
||||||
|
* Supports:
|
||||||
|
* - cuts: only one, 6th cut (represented by 6th bit).
|
||||||
|
* - substrate: supports 16 different substrate versions
|
||||||
|
* - process: supports 9 different process versions
|
||||||
|
*/
|
||||||
|
opp-supported-hw = <0x20 0xff0000ff 0x0000f4f0>
|
||||||
opp-hz = /bits/ 64 <800000000>;
|
opp-hz = /bits/ 64 <800000000>;
|
||||||
...
|
opp-microvolt = <900000 915000 925000>;
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
cpu0_opp_table_fast: opp_table_fast {
|
|
||||||
compatible = "operating-points-v2";
|
|
||||||
status = "okay";
|
|
||||||
opp-shared;
|
|
||||||
|
|
||||||
opp10 {
|
|
||||||
opp-hz = /bits/ 64 <1000000000>;
|
|
||||||
...
|
|
||||||
};
|
|
||||||
|
|
||||||
opp11 {
|
|
||||||
opp-hz = /bits/ 64 <1100000000>;
|
|
||||||
...
|
...
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Example 6: opp-microvolt-<name>, opp-microamp-<name>:
|
||||||
|
(example: device with two possible microvolt ranges: slow and fast)
|
||||||
|
|
||||||
|
/ {
|
||||||
|
cpus {
|
||||||
|
cpu@0 {
|
||||||
|
compatible = "arm,cortex-a7";
|
||||||
|
...
|
||||||
|
|
||||||
|
operating-points-v2 = <&cpu0_opp_table>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
cpu0_opp_table: opp_table0 {
|
||||||
|
compatible = "operating-points-v2";
|
||||||
|
opp-shared;
|
||||||
|
|
||||||
|
opp@1000000000 {
|
||||||
|
opp-hz = /bits/ 64 <1000000000>;
|
||||||
|
opp-microvolt-slow = <900000 915000 925000>;
|
||||||
|
opp-microvolt-fast = <970000 975000 985000>;
|
||||||
|
opp-microamp-slow = <70000>;
|
||||||
|
opp-microamp-fast = <71000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
opp@1200000000 {
|
||||||
|
opp-hz = /bits/ 64 <1200000000>;
|
||||||
|
opp-microvolt-slow = <900000 915000 925000>, /* Supply vcc0 */
|
||||||
|
<910000 925000 935000>; /* Supply vcc1 */
|
||||||
|
opp-microvolt-fast = <970000 975000 985000>, /* Supply vcc0 */
|
||||||
|
<960000 965000 975000>; /* Supply vcc1 */
|
||||||
|
opp-microamp = <70000>; /* Will be used for both slow/fast */
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -64,73 +64,73 @@
|
||||||
compatible = "operating-points-v2";
|
compatible = "operating-points-v2";
|
||||||
opp-shared;
|
opp-shared;
|
||||||
|
|
||||||
opp00 {
|
opp@200000000 {
|
||||||
opp-hz = /bits/ 64 <200000000>;
|
opp-hz = /bits/ 64 <200000000>;
|
||||||
opp-microvolt = <900000>;
|
opp-microvolt = <900000>;
|
||||||
clock-latency-ns = <200000>;
|
clock-latency-ns = <200000>;
|
||||||
};
|
};
|
||||||
opp01 {
|
opp@300000000 {
|
||||||
opp-hz = /bits/ 64 <300000000>;
|
opp-hz = /bits/ 64 <300000000>;
|
||||||
opp-microvolt = <900000>;
|
opp-microvolt = <900000>;
|
||||||
clock-latency-ns = <200000>;
|
clock-latency-ns = <200000>;
|
||||||
};
|
};
|
||||||
opp02 {
|
opp@400000000 {
|
||||||
opp-hz = /bits/ 64 <400000000>;
|
opp-hz = /bits/ 64 <400000000>;
|
||||||
opp-microvolt = <925000>;
|
opp-microvolt = <925000>;
|
||||||
clock-latency-ns = <200000>;
|
clock-latency-ns = <200000>;
|
||||||
};
|
};
|
||||||
opp03 {
|
opp@500000000 {
|
||||||
opp-hz = /bits/ 64 <500000000>;
|
opp-hz = /bits/ 64 <500000000>;
|
||||||
opp-microvolt = <950000>;
|
opp-microvolt = <950000>;
|
||||||
clock-latency-ns = <200000>;
|
clock-latency-ns = <200000>;
|
||||||
};
|
};
|
||||||
opp04 {
|
opp@600000000 {
|
||||||
opp-hz = /bits/ 64 <600000000>;
|
opp-hz = /bits/ 64 <600000000>;
|
||||||
opp-microvolt = <975000>;
|
opp-microvolt = <975000>;
|
||||||
clock-latency-ns = <200000>;
|
clock-latency-ns = <200000>;
|
||||||
};
|
};
|
||||||
opp05 {
|
opp@700000000 {
|
||||||
opp-hz = /bits/ 64 <700000000>;
|
opp-hz = /bits/ 64 <700000000>;
|
||||||
opp-microvolt = <987500>;
|
opp-microvolt = <987500>;
|
||||||
clock-latency-ns = <200000>;
|
clock-latency-ns = <200000>;
|
||||||
};
|
};
|
||||||
opp06 {
|
opp@800000000 {
|
||||||
opp-hz = /bits/ 64 <800000000>;
|
opp-hz = /bits/ 64 <800000000>;
|
||||||
opp-microvolt = <1000000>;
|
opp-microvolt = <1000000>;
|
||||||
clock-latency-ns = <200000>;
|
clock-latency-ns = <200000>;
|
||||||
opp-suspend;
|
opp-suspend;
|
||||||
};
|
};
|
||||||
opp07 {
|
opp@900000000 {
|
||||||
opp-hz = /bits/ 64 <900000000>;
|
opp-hz = /bits/ 64 <900000000>;
|
||||||
opp-microvolt = <1037500>;
|
opp-microvolt = <1037500>;
|
||||||
clock-latency-ns = <200000>;
|
clock-latency-ns = <200000>;
|
||||||
};
|
};
|
||||||
opp08 {
|
opp@1000000000 {
|
||||||
opp-hz = /bits/ 64 <1000000000>;
|
opp-hz = /bits/ 64 <1000000000>;
|
||||||
opp-microvolt = <1087500>;
|
opp-microvolt = <1087500>;
|
||||||
clock-latency-ns = <200000>;
|
clock-latency-ns = <200000>;
|
||||||
};
|
};
|
||||||
opp09 {
|
opp@1100000000 {
|
||||||
opp-hz = /bits/ 64 <1100000000>;
|
opp-hz = /bits/ 64 <1100000000>;
|
||||||
opp-microvolt = <1137500>;
|
opp-microvolt = <1137500>;
|
||||||
clock-latency-ns = <200000>;
|
clock-latency-ns = <200000>;
|
||||||
};
|
};
|
||||||
opp10 {
|
opp@1200000000 {
|
||||||
opp-hz = /bits/ 64 <1200000000>;
|
opp-hz = /bits/ 64 <1200000000>;
|
||||||
opp-microvolt = <1187500>;
|
opp-microvolt = <1187500>;
|
||||||
clock-latency-ns = <200000>;
|
clock-latency-ns = <200000>;
|
||||||
};
|
};
|
||||||
opp11 {
|
opp@1300000000 {
|
||||||
opp-hz = /bits/ 64 <1300000000>;
|
opp-hz = /bits/ 64 <1300000000>;
|
||||||
opp-microvolt = <1250000>;
|
opp-microvolt = <1250000>;
|
||||||
clock-latency-ns = <200000>;
|
clock-latency-ns = <200000>;
|
||||||
};
|
};
|
||||||
opp12 {
|
opp@1400000000 {
|
||||||
opp-hz = /bits/ 64 <1400000000>;
|
opp-hz = /bits/ 64 <1400000000>;
|
||||||
opp-microvolt = <1287500>;
|
opp-microvolt = <1287500>;
|
||||||
clock-latency-ns = <200000>;
|
clock-latency-ns = <200000>;
|
||||||
};
|
};
|
||||||
opp13 {
|
opp@1500000000 {
|
||||||
opp-hz = /bits/ 64 <1500000000>;
|
opp-hz = /bits/ 64 <1500000000>;
|
||||||
opp-microvolt = <1350000>;
|
opp-microvolt = <1350000>;
|
||||||
clock-latency-ns = <200000>;
|
clock-latency-ns = <200000>;
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
|
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
|
||||||
obj-y += core.o cpu.o
|
obj-y += core.o cpu.o
|
||||||
|
obj-$(CONFIG_DEBUG_FS) += debugfs.o
|
||||||
|
|
|
@ -463,6 +463,7 @@ static void _kfree_list_dev_rcu(struct rcu_head *head)
|
||||||
static void _remove_list_dev(struct device_list_opp *list_dev,
|
static void _remove_list_dev(struct device_list_opp *list_dev,
|
||||||
struct device_opp *dev_opp)
|
struct device_opp *dev_opp)
|
||||||
{
|
{
|
||||||
|
opp_debug_unregister(list_dev, dev_opp);
|
||||||
list_del(&list_dev->node);
|
list_del(&list_dev->node);
|
||||||
call_srcu(&dev_opp->srcu_head.srcu, &list_dev->rcu_head,
|
call_srcu(&dev_opp->srcu_head.srcu, &list_dev->rcu_head,
|
||||||
_kfree_list_dev_rcu);
|
_kfree_list_dev_rcu);
|
||||||
|
@ -472,6 +473,7 @@ struct device_list_opp *_add_list_dev(const struct device *dev,
|
||||||
struct device_opp *dev_opp)
|
struct device_opp *dev_opp)
|
||||||
{
|
{
|
||||||
struct device_list_opp *list_dev;
|
struct device_list_opp *list_dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
list_dev = kzalloc(sizeof(*list_dev), GFP_KERNEL);
|
list_dev = kzalloc(sizeof(*list_dev), GFP_KERNEL);
|
||||||
if (!list_dev)
|
if (!list_dev)
|
||||||
|
@ -481,6 +483,12 @@ struct device_list_opp *_add_list_dev(const struct device *dev,
|
||||||
list_dev->dev = dev;
|
list_dev->dev = dev;
|
||||||
list_add_rcu(&list_dev->node, &dev_opp->dev_list);
|
list_add_rcu(&list_dev->node, &dev_opp->dev_list);
|
||||||
|
|
||||||
|
/* Create debugfs entries for the dev_opp */
|
||||||
|
ret = opp_debug_register(list_dev, dev_opp);
|
||||||
|
if (ret)
|
||||||
|
dev_err(dev, "%s: Failed to register opp debugfs (%d)\n",
|
||||||
|
__func__, ret);
|
||||||
|
|
||||||
return list_dev;
|
return list_dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -551,6 +559,12 @@ static void _remove_device_opp(struct device_opp *dev_opp)
|
||||||
if (!list_empty(&dev_opp->opp_list))
|
if (!list_empty(&dev_opp->opp_list))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (dev_opp->supported_hw)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (dev_opp->prop_name)
|
||||||
|
return;
|
||||||
|
|
||||||
list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp,
|
list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp,
|
||||||
node);
|
node);
|
||||||
|
|
||||||
|
@ -596,6 +610,7 @@ static void _opp_remove(struct device_opp *dev_opp,
|
||||||
*/
|
*/
|
||||||
if (notify)
|
if (notify)
|
||||||
srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_REMOVE, opp);
|
srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_REMOVE, opp);
|
||||||
|
opp_debug_remove_one(opp);
|
||||||
list_del_rcu(&opp->node);
|
list_del_rcu(&opp->node);
|
||||||
call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu);
|
call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu);
|
||||||
|
|
||||||
|
@ -673,6 +688,7 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
|
||||||
{
|
{
|
||||||
struct dev_pm_opp *opp;
|
struct dev_pm_opp *opp;
|
||||||
struct list_head *head = &dev_opp->opp_list;
|
struct list_head *head = &dev_opp->opp_list;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Insert new OPP in order of increasing frequency and discard if
|
* Insert new OPP in order of increasing frequency and discard if
|
||||||
|
@ -703,6 +719,11 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
|
||||||
new_opp->dev_opp = dev_opp;
|
new_opp->dev_opp = dev_opp;
|
||||||
list_add_rcu(&new_opp->node, head);
|
list_add_rcu(&new_opp->node, head);
|
||||||
|
|
||||||
|
ret = opp_debug_create_one(new_opp, dev_opp);
|
||||||
|
if (ret)
|
||||||
|
dev_err(dev, "%s: Failed to register opp to debugfs (%d)\n",
|
||||||
|
__func__, ret);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -776,35 +797,49 @@ unlock:
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Support multiple regulators */
|
/* TODO: Support multiple regulators */
|
||||||
static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev)
|
static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
|
||||||
|
struct device_opp *dev_opp)
|
||||||
{
|
{
|
||||||
u32 microvolt[3] = {0};
|
u32 microvolt[3] = {0};
|
||||||
u32 val;
|
u32 val;
|
||||||
int count, ret;
|
int count, ret;
|
||||||
|
struct property *prop = NULL;
|
||||||
|
char name[NAME_MAX];
|
||||||
|
|
||||||
/* Missing property isn't a problem, but an invalid entry is */
|
/* Search for "opp-microvolt-<name>" */
|
||||||
if (!of_find_property(opp->np, "opp-microvolt", NULL))
|
if (dev_opp->prop_name) {
|
||||||
return 0;
|
snprintf(name, sizeof(name), "opp-microvolt-%s",
|
||||||
|
dev_opp->prop_name);
|
||||||
|
prop = of_find_property(opp->np, name, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
count = of_property_count_u32_elems(opp->np, "opp-microvolt");
|
if (!prop) {
|
||||||
|
/* Search for "opp-microvolt" */
|
||||||
|
sprintf(name, "opp-microvolt");
|
||||||
|
prop = of_find_property(opp->np, name, NULL);
|
||||||
|
|
||||||
|
/* Missing property isn't a problem, but an invalid entry is */
|
||||||
|
if (!prop)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = of_property_count_u32_elems(opp->np, name);
|
||||||
if (count < 0) {
|
if (count < 0) {
|
||||||
dev_err(dev, "%s: Invalid opp-microvolt property (%d)\n",
|
dev_err(dev, "%s: Invalid %s property (%d)\n",
|
||||||
__func__, count);
|
__func__, name, count);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* There can be one or three elements here */
|
/* There can be one or three elements here */
|
||||||
if (count != 1 && count != 3) {
|
if (count != 1 && count != 3) {
|
||||||
dev_err(dev, "%s: Invalid number of elements in opp-microvolt property (%d)\n",
|
dev_err(dev, "%s: Invalid number of elements in %s property (%d)\n",
|
||||||
__func__, count);
|
__func__, name, count);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = of_property_read_u32_array(opp->np, "opp-microvolt", microvolt,
|
ret = of_property_read_u32_array(opp->np, name, microvolt, count);
|
||||||
count);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "%s: error parsing opp-microvolt: %d\n", __func__,
|
dev_err(dev, "%s: error parsing %s: %d\n", __func__, name, ret);
|
||||||
ret);
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -812,12 +847,271 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev)
|
||||||
opp->u_volt_min = microvolt[1];
|
opp->u_volt_min = microvolt[1];
|
||||||
opp->u_volt_max = microvolt[2];
|
opp->u_volt_max = microvolt[2];
|
||||||
|
|
||||||
if (!of_property_read_u32(opp->np, "opp-microamp", &val))
|
/* Search for "opp-microamp-<name>" */
|
||||||
|
prop = NULL;
|
||||||
|
if (dev_opp->prop_name) {
|
||||||
|
snprintf(name, sizeof(name), "opp-microamp-%s",
|
||||||
|
dev_opp->prop_name);
|
||||||
|
prop = of_find_property(opp->np, name, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!prop) {
|
||||||
|
/* Search for "opp-microamp" */
|
||||||
|
sprintf(name, "opp-microamp");
|
||||||
|
prop = of_find_property(opp->np, name, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prop && !of_property_read_u32(opp->np, name, &val))
|
||||||
opp->u_amp = val;
|
opp->u_amp = val;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dev_pm_opp_set_supported_hw() - Set supported platforms
|
||||||
|
* @dev: Device for which supported-hw has to be set.
|
||||||
|
* @versions: Array of hierarchy of versions to match.
|
||||||
|
* @count: Number of elements in the array.
|
||||||
|
*
|
||||||
|
* This is required only for the V2 bindings, and it enables a platform to
|
||||||
|
* specify the hierarchy of versions it supports. OPP layer will then enable
|
||||||
|
* OPPs, which are available for those versions, based on its 'opp-supported-hw'
|
||||||
|
* property.
|
||||||
|
*
|
||||||
|
* Locking: The internal device_opp and opp structures are RCU protected.
|
||||||
|
* Hence this function internally uses RCU updater strategy with mutex locks
|
||||||
|
* to keep the integrity of the internal data structures. Callers should ensure
|
||||||
|
* that this function is *NOT* called under RCU protection or in contexts where
|
||||||
|
* mutex cannot be locked.
|
||||||
|
*/
|
||||||
|
int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions,
|
||||||
|
unsigned int count)
|
||||||
|
{
|
||||||
|
struct device_opp *dev_opp;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/* Hold our list modification lock here */
|
||||||
|
mutex_lock(&dev_opp_list_lock);
|
||||||
|
|
||||||
|
dev_opp = _add_device_opp(dev);
|
||||||
|
if (!dev_opp) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure there are no concurrent readers while updating dev_opp */
|
||||||
|
WARN_ON(!list_empty(&dev_opp->opp_list));
|
||||||
|
|
||||||
|
/* Do we already have a version hierarchy associated with dev_opp? */
|
||||||
|
if (dev_opp->supported_hw) {
|
||||||
|
dev_err(dev, "%s: Already have supported hardware list\n",
|
||||||
|
__func__);
|
||||||
|
ret = -EBUSY;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_opp->supported_hw = kmemdup(versions, count * sizeof(*versions),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!dev_opp->supported_hw) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_opp->supported_hw_count = count;
|
||||||
|
mutex_unlock(&dev_opp_list_lock);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
_remove_device_opp(dev_opp);
|
||||||
|
unlock:
|
||||||
|
mutex_unlock(&dev_opp_list_lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(dev_pm_opp_set_supported_hw);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dev_pm_opp_put_supported_hw() - Releases resources blocked for supported hw
|
||||||
|
* @dev: Device for which supported-hw has to be set.
|
||||||
|
*
|
||||||
|
* This is required only for the V2 bindings, and is called for a matching
|
||||||
|
* dev_pm_opp_set_supported_hw(). Until this is called, the device_opp structure
|
||||||
|
* will not be freed.
|
||||||
|
*
|
||||||
|
* Locking: The internal device_opp and opp structures are RCU protected.
|
||||||
|
* Hence this function internally uses RCU updater strategy with mutex locks
|
||||||
|
* to keep the integrity of the internal data structures. Callers should ensure
|
||||||
|
* that this function is *NOT* called under RCU protection or in contexts where
|
||||||
|
* mutex cannot be locked.
|
||||||
|
*/
|
||||||
|
void dev_pm_opp_put_supported_hw(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_opp *dev_opp;
|
||||||
|
|
||||||
|
/* Hold our list modification lock here */
|
||||||
|
mutex_lock(&dev_opp_list_lock);
|
||||||
|
|
||||||
|
/* Check for existing list for 'dev' first */
|
||||||
|
dev_opp = _find_device_opp(dev);
|
||||||
|
if (IS_ERR(dev_opp)) {
|
||||||
|
dev_err(dev, "Failed to find dev_opp: %ld\n", PTR_ERR(dev_opp));
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure there are no concurrent readers while updating dev_opp */
|
||||||
|
WARN_ON(!list_empty(&dev_opp->opp_list));
|
||||||
|
|
||||||
|
if (!dev_opp->supported_hw) {
|
||||||
|
dev_err(dev, "%s: Doesn't have supported hardware list\n",
|
||||||
|
__func__);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(dev_opp->supported_hw);
|
||||||
|
dev_opp->supported_hw = NULL;
|
||||||
|
dev_opp->supported_hw_count = 0;
|
||||||
|
|
||||||
|
/* Try freeing device_opp if this was the last blocking resource */
|
||||||
|
_remove_device_opp(dev_opp);
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
mutex_unlock(&dev_opp_list_lock);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dev_pm_opp_set_prop_name() - Set prop-extn name
|
||||||
|
* @dev: Device for which the regulator has to be set.
|
||||||
|
* @name: name to postfix to properties.
|
||||||
|
*
|
||||||
|
* This is required only for the V2 bindings, and it enables a platform to
|
||||||
|
* specify the extn to be used for certain property names. The properties to
|
||||||
|
* which the extension will apply are opp-microvolt and opp-microamp. OPP core
|
||||||
|
* should postfix the property name with -<name> while looking for them.
|
||||||
|
*
|
||||||
|
* Locking: The internal device_opp and opp structures are RCU protected.
|
||||||
|
* Hence this function internally uses RCU updater strategy with mutex locks
|
||||||
|
* to keep the integrity of the internal data structures. Callers should ensure
|
||||||
|
* that this function is *NOT* called under RCU protection or in contexts where
|
||||||
|
* mutex cannot be locked.
|
||||||
|
*/
|
||||||
|
int dev_pm_opp_set_prop_name(struct device *dev, const char *name)
|
||||||
|
{
|
||||||
|
struct device_opp *dev_opp;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/* Hold our list modification lock here */
|
||||||
|
mutex_lock(&dev_opp_list_lock);
|
||||||
|
|
||||||
|
dev_opp = _add_device_opp(dev);
|
||||||
|
if (!dev_opp) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure there are no concurrent readers while updating dev_opp */
|
||||||
|
WARN_ON(!list_empty(&dev_opp->opp_list));
|
||||||
|
|
||||||
|
/* Do we already have a prop-name associated with dev_opp? */
|
||||||
|
if (dev_opp->prop_name) {
|
||||||
|
dev_err(dev, "%s: Already have prop-name %s\n", __func__,
|
||||||
|
dev_opp->prop_name);
|
||||||
|
ret = -EBUSY;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_opp->prop_name = kstrdup(name, GFP_KERNEL);
|
||||||
|
if (!dev_opp->prop_name) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&dev_opp_list_lock);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
_remove_device_opp(dev_opp);
|
||||||
|
unlock:
|
||||||
|
mutex_unlock(&dev_opp_list_lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(dev_pm_opp_set_prop_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dev_pm_opp_put_prop_name() - Releases resources blocked for prop-name
|
||||||
|
* @dev: Device for which the regulator has to be set.
|
||||||
|
*
|
||||||
|
* This is required only for the V2 bindings, and is called for a matching
|
||||||
|
* dev_pm_opp_set_prop_name(). Until this is called, the device_opp structure
|
||||||
|
* will not be freed.
|
||||||
|
*
|
||||||
|
* Locking: The internal device_opp and opp structures are RCU protected.
|
||||||
|
* Hence this function internally uses RCU updater strategy with mutex locks
|
||||||
|
* to keep the integrity of the internal data structures. Callers should ensure
|
||||||
|
* that this function is *NOT* called under RCU protection or in contexts where
|
||||||
|
* mutex cannot be locked.
|
||||||
|
*/
|
||||||
|
void dev_pm_opp_put_prop_name(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_opp *dev_opp;
|
||||||
|
|
||||||
|
/* Hold our list modification lock here */
|
||||||
|
mutex_lock(&dev_opp_list_lock);
|
||||||
|
|
||||||
|
/* Check for existing list for 'dev' first */
|
||||||
|
dev_opp = _find_device_opp(dev);
|
||||||
|
if (IS_ERR(dev_opp)) {
|
||||||
|
dev_err(dev, "Failed to find dev_opp: %ld\n", PTR_ERR(dev_opp));
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure there are no concurrent readers while updating dev_opp */
|
||||||
|
WARN_ON(!list_empty(&dev_opp->opp_list));
|
||||||
|
|
||||||
|
if (!dev_opp->prop_name) {
|
||||||
|
dev_err(dev, "%s: Doesn't have a prop-name\n", __func__);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(dev_opp->prop_name);
|
||||||
|
dev_opp->prop_name = NULL;
|
||||||
|
|
||||||
|
/* Try freeing device_opp if this was the last blocking resource */
|
||||||
|
_remove_device_opp(dev_opp);
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
mutex_unlock(&dev_opp_list_lock);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name);
|
||||||
|
|
||||||
|
static bool _opp_is_supported(struct device *dev, struct device_opp *dev_opp,
|
||||||
|
struct device_node *np)
|
||||||
|
{
|
||||||
|
unsigned int count = dev_opp->supported_hw_count;
|
||||||
|
u32 version;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!dev_opp->supported_hw)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
while (count--) {
|
||||||
|
ret = of_property_read_u32_index(np, "opp-supported-hw", count,
|
||||||
|
&version);
|
||||||
|
if (ret) {
|
||||||
|
dev_warn(dev, "%s: failed to read opp-supported-hw property at index %d: %d\n",
|
||||||
|
__func__, count, ret);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Both of these are bitwise masks of the versions */
|
||||||
|
if (!(version & dev_opp->supported_hw[count]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings)
|
* _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings)
|
||||||
* @dev: device for which we do this operation
|
* @dev: device for which we do this operation
|
||||||
|
@ -864,6 +1158,12 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
|
||||||
goto free_opp;
|
goto free_opp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check if the OPP supports hardware's hierarchy of versions or not */
|
||||||
|
if (!_opp_is_supported(dev, dev_opp, np)) {
|
||||||
|
dev_dbg(dev, "OPP not supported by hardware: %llu\n", rate);
|
||||||
|
goto free_opp;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Rate is defined as an unsigned long in clk API, and so casting
|
* Rate is defined as an unsigned long in clk API, and so casting
|
||||||
* explicitly to its type. Must be fixed once rate is 64 bit
|
* explicitly to its type. Must be fixed once rate is 64 bit
|
||||||
|
@ -879,7 +1179,7 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
|
||||||
if (!of_property_read_u32(np, "clock-latency-ns", &val))
|
if (!of_property_read_u32(np, "clock-latency-ns", &val))
|
||||||
new_opp->clock_latency_ns = val;
|
new_opp->clock_latency_ns = val;
|
||||||
|
|
||||||
ret = opp_parse_supplies(new_opp, dev);
|
ret = opp_parse_supplies(new_opp, dev, dev_opp);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto free_opp;
|
goto free_opp;
|
||||||
|
|
||||||
|
@ -889,12 +1189,14 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
|
||||||
|
|
||||||
/* OPP to select on device suspend */
|
/* OPP to select on device suspend */
|
||||||
if (of_property_read_bool(np, "opp-suspend")) {
|
if (of_property_read_bool(np, "opp-suspend")) {
|
||||||
if (dev_opp->suspend_opp)
|
if (dev_opp->suspend_opp) {
|
||||||
dev_warn(dev, "%s: Multiple suspend OPPs found (%lu %lu)\n",
|
dev_warn(dev, "%s: Multiple suspend OPPs found (%lu %lu)\n",
|
||||||
__func__, dev_opp->suspend_opp->rate,
|
__func__, dev_opp->suspend_opp->rate,
|
||||||
new_opp->rate);
|
new_opp->rate);
|
||||||
else
|
} else {
|
||||||
|
new_opp->suspend = true;
|
||||||
dev_opp->suspend_opp = new_opp;
|
dev_opp->suspend_opp = new_opp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_opp->clock_latency_ns > dev_opp->clock_latency_ns_max)
|
if (new_opp->clock_latency_ns > dev_opp->clock_latency_ns_max)
|
||||||
|
|
|
@ -214,7 +214,6 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table);
|
||||||
/*
|
/*
|
||||||
* Works only for OPP v2 bindings.
|
* Works only for OPP v2 bindings.
|
||||||
*
|
*
|
||||||
* cpumask should be already set to mask of cpu_dev->id.
|
|
||||||
* Returns -ENOENT if operating-points-v2 bindings aren't supported.
|
* Returns -ENOENT if operating-points-v2 bindings aren't supported.
|
||||||
*/
|
*/
|
||||||
int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask)
|
int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask)
|
||||||
|
@ -230,6 +229,8 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cpumask_set_cpu(cpu_dev->id, cpumask);
|
||||||
|
|
||||||
/* OPPs are shared ? */
|
/* OPPs are shared ? */
|
||||||
if (!of_property_read_bool(np, "opp-shared"))
|
if (!of_property_read_bool(np, "opp-shared"))
|
||||||
goto put_cpu_node;
|
goto put_cpu_node;
|
||||||
|
|
|
@ -0,0 +1,219 @@
|
||||||
|
/*
|
||||||
|
* Generic OPP debugfs interface
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015-2016 Viresh Kumar <viresh.kumar@linaro.org>
|
||||||
|
*
|
||||||
|
* 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) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
|
#include <linux/debugfs.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
|
|
||||||
|
#include "opp.h"
|
||||||
|
|
||||||
|
static struct dentry *rootdir;
|
||||||
|
|
||||||
|
static void opp_set_dev_name(const struct device *dev, char *name)
|
||||||
|
{
|
||||||
|
if (dev->parent)
|
||||||
|
snprintf(name, NAME_MAX, "%s-%s", dev_name(dev->parent),
|
||||||
|
dev_name(dev));
|
||||||
|
else
|
||||||
|
snprintf(name, NAME_MAX, "%s", dev_name(dev));
|
||||||
|
}
|
||||||
|
|
||||||
|
void opp_debug_remove_one(struct dev_pm_opp *opp)
|
||||||
|
{
|
||||||
|
debugfs_remove_recursive(opp->dentry);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opp_debug_create_one(struct dev_pm_opp *opp, struct device_opp *dev_opp)
|
||||||
|
{
|
||||||
|
struct dentry *pdentry = dev_opp->dentry;
|
||||||
|
struct dentry *d;
|
||||||
|
char name[25]; /* 20 chars for 64 bit value + 5 (opp:\0) */
|
||||||
|
|
||||||
|
/* Rate is unique to each OPP, use it to give opp-name */
|
||||||
|
snprintf(name, sizeof(name), "opp:%lu", opp->rate);
|
||||||
|
|
||||||
|
/* Create per-opp directory */
|
||||||
|
d = debugfs_create_dir(name, pdentry);
|
||||||
|
if (!d)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (!debugfs_create_bool("available", S_IRUGO, d, &opp->available))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (!debugfs_create_bool("dynamic", S_IRUGO, d, &opp->dynamic))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (!debugfs_create_bool("turbo", S_IRUGO, d, &opp->turbo))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (!debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (!debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (!debugfs_create_ulong("u_volt_target", S_IRUGO, d, &opp->u_volt))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (!debugfs_create_ulong("u_volt_min", S_IRUGO, d, &opp->u_volt_min))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (!debugfs_create_ulong("u_volt_max", S_IRUGO, d, &opp->u_volt_max))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (!debugfs_create_ulong("u_amp", S_IRUGO, d, &opp->u_amp))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (!debugfs_create_ulong("clock_latency_ns", S_IRUGO, d,
|
||||||
|
&opp->clock_latency_ns))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
opp->dentry = d;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int device_opp_debug_create_dir(struct device_list_opp *list_dev,
|
||||||
|
struct device_opp *dev_opp)
|
||||||
|
{
|
||||||
|
const struct device *dev = list_dev->dev;
|
||||||
|
struct dentry *d;
|
||||||
|
|
||||||
|
opp_set_dev_name(dev, dev_opp->dentry_name);
|
||||||
|
|
||||||
|
/* Create device specific directory */
|
||||||
|
d = debugfs_create_dir(dev_opp->dentry_name, rootdir);
|
||||||
|
if (!d) {
|
||||||
|
dev_err(dev, "%s: Failed to create debugfs dir\n", __func__);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_dev->dentry = d;
|
||||||
|
dev_opp->dentry = d;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int device_opp_debug_create_link(struct device_list_opp *list_dev,
|
||||||
|
struct device_opp *dev_opp)
|
||||||
|
{
|
||||||
|
const struct device *dev = list_dev->dev;
|
||||||
|
char name[NAME_MAX];
|
||||||
|
struct dentry *d;
|
||||||
|
|
||||||
|
opp_set_dev_name(list_dev->dev, name);
|
||||||
|
|
||||||
|
/* Create device specific directory link */
|
||||||
|
d = debugfs_create_symlink(name, rootdir, dev_opp->dentry_name);
|
||||||
|
if (!d) {
|
||||||
|
dev_err(dev, "%s: Failed to create link\n", __func__);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_dev->dentry = d;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* opp_debug_register - add a device opp node to the debugfs 'opp' directory
|
||||||
|
* @list_dev: list-dev pointer for device
|
||||||
|
* @dev_opp: the device-opp being added
|
||||||
|
*
|
||||||
|
* Dynamically adds device specific directory in debugfs 'opp' directory. If the
|
||||||
|
* device-opp is shared with other devices, then links will be created for all
|
||||||
|
* devices except the first.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, otherwise negative error.
|
||||||
|
*/
|
||||||
|
int opp_debug_register(struct device_list_opp *list_dev,
|
||||||
|
struct device_opp *dev_opp)
|
||||||
|
{
|
||||||
|
if (!rootdir) {
|
||||||
|
pr_debug("%s: Uninitialized rootdir\n", __func__);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dev_opp->dentry)
|
||||||
|
return device_opp_debug_create_link(list_dev, dev_opp);
|
||||||
|
|
||||||
|
return device_opp_debug_create_dir(list_dev, dev_opp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void opp_migrate_dentry(struct device_list_opp *list_dev,
|
||||||
|
struct device_opp *dev_opp)
|
||||||
|
{
|
||||||
|
struct device_list_opp *new_dev;
|
||||||
|
const struct device *dev;
|
||||||
|
struct dentry *dentry;
|
||||||
|
|
||||||
|
/* Look for next list-dev */
|
||||||
|
list_for_each_entry(new_dev, &dev_opp->dev_list, node)
|
||||||
|
if (new_dev != list_dev)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* new_dev is guaranteed to be valid here */
|
||||||
|
dev = new_dev->dev;
|
||||||
|
debugfs_remove_recursive(new_dev->dentry);
|
||||||
|
|
||||||
|
opp_set_dev_name(dev, dev_opp->dentry_name);
|
||||||
|
|
||||||
|
dentry = debugfs_rename(rootdir, list_dev->dentry, rootdir,
|
||||||
|
dev_opp->dentry_name);
|
||||||
|
if (!dentry) {
|
||||||
|
dev_err(dev, "%s: Failed to rename link from: %s to %s\n",
|
||||||
|
__func__, dev_name(list_dev->dev), dev_name(dev));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_dev->dentry = dentry;
|
||||||
|
dev_opp->dentry = dentry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* opp_debug_unregister - remove a device opp node from debugfs opp directory
|
||||||
|
* @list_dev: list-dev pointer for device
|
||||||
|
* @dev_opp: the device-opp being removed
|
||||||
|
*
|
||||||
|
* Dynamically removes device specific directory from debugfs 'opp' directory.
|
||||||
|
*/
|
||||||
|
void opp_debug_unregister(struct device_list_opp *list_dev,
|
||||||
|
struct device_opp *dev_opp)
|
||||||
|
{
|
||||||
|
if (list_dev->dentry == dev_opp->dentry) {
|
||||||
|
/* Move the real dentry object under another device */
|
||||||
|
if (!list_is_singular(&dev_opp->dev_list)) {
|
||||||
|
opp_migrate_dentry(list_dev, dev_opp);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
dev_opp->dentry = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
debugfs_remove_recursive(list_dev->dentry);
|
||||||
|
|
||||||
|
out:
|
||||||
|
list_dev->dentry = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init opp_debug_init(void)
|
||||||
|
{
|
||||||
|
/* Create /sys/kernel/debug/opp directory */
|
||||||
|
rootdir = debugfs_create_dir("opp", NULL);
|
||||||
|
if (!rootdir) {
|
||||||
|
pr_err("%s: Failed to create root directory\n", __func__);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
core_initcall(opp_debug_init);
|
|
@ -17,6 +17,7 @@
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
#include <linux/pm_opp.h>
|
#include <linux/pm_opp.h>
|
||||||
#include <linux/rculist.h>
|
#include <linux/rculist.h>
|
||||||
#include <linux/rcupdate.h>
|
#include <linux/rcupdate.h>
|
||||||
|
@ -50,9 +51,10 @@ extern struct mutex dev_opp_list_lock;
|
||||||
* are protected by the dev_opp_list_lock for integrity.
|
* are protected by the dev_opp_list_lock for integrity.
|
||||||
* IMPORTANT: the opp nodes should be maintained in increasing
|
* IMPORTANT: the opp nodes should be maintained in increasing
|
||||||
* order.
|
* order.
|
||||||
* @dynamic: not-created from static DT entries.
|
|
||||||
* @available: true/false - marks if this OPP as available or not
|
* @available: true/false - marks if this OPP as available or not
|
||||||
|
* @dynamic: not-created from static DT entries.
|
||||||
* @turbo: true if turbo (boost) OPP
|
* @turbo: true if turbo (boost) OPP
|
||||||
|
* @suspend: true if suspend OPP
|
||||||
* @rate: Frequency in hertz
|
* @rate: Frequency in hertz
|
||||||
* @u_volt: Target voltage in microvolts corresponding to this OPP
|
* @u_volt: Target voltage in microvolts corresponding to this OPP
|
||||||
* @u_volt_min: Minimum voltage in microvolts corresponding to this OPP
|
* @u_volt_min: Minimum voltage in microvolts corresponding to this OPP
|
||||||
|
@ -63,6 +65,7 @@ extern struct mutex dev_opp_list_lock;
|
||||||
* @dev_opp: points back to the device_opp struct this opp belongs to
|
* @dev_opp: points back to the device_opp struct this opp belongs to
|
||||||
* @rcu_head: RCU callback head used for deferred freeing
|
* @rcu_head: RCU callback head used for deferred freeing
|
||||||
* @np: OPP's device node.
|
* @np: OPP's device node.
|
||||||
|
* @dentry: debugfs dentry pointer (per opp)
|
||||||
*
|
*
|
||||||
* This structure stores the OPP information for a given device.
|
* This structure stores the OPP information for a given device.
|
||||||
*/
|
*/
|
||||||
|
@ -72,6 +75,7 @@ struct dev_pm_opp {
|
||||||
bool available;
|
bool available;
|
||||||
bool dynamic;
|
bool dynamic;
|
||||||
bool turbo;
|
bool turbo;
|
||||||
|
bool suspend;
|
||||||
unsigned long rate;
|
unsigned long rate;
|
||||||
|
|
||||||
unsigned long u_volt;
|
unsigned long u_volt;
|
||||||
|
@ -84,6 +88,10 @@ struct dev_pm_opp {
|
||||||
struct rcu_head rcu_head;
|
struct rcu_head rcu_head;
|
||||||
|
|
||||||
struct device_node *np;
|
struct device_node *np;
|
||||||
|
|
||||||
|
#ifdef CONFIG_DEBUG_FS
|
||||||
|
struct dentry *dentry;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -91,6 +99,7 @@ struct dev_pm_opp {
|
||||||
* @node: list node
|
* @node: list node
|
||||||
* @dev: device to which the struct object belongs
|
* @dev: device to which the struct object belongs
|
||||||
* @rcu_head: RCU callback head used for deferred freeing
|
* @rcu_head: RCU callback head used for deferred freeing
|
||||||
|
* @dentry: debugfs dentry pointer (per device)
|
||||||
*
|
*
|
||||||
* This is an internal data structure maintaining the list of devices that are
|
* This is an internal data structure maintaining the list of devices that are
|
||||||
* managed by 'struct device_opp'.
|
* managed by 'struct device_opp'.
|
||||||
|
@ -99,6 +108,10 @@ struct device_list_opp {
|
||||||
struct list_head node;
|
struct list_head node;
|
||||||
const struct device *dev;
|
const struct device *dev;
|
||||||
struct rcu_head rcu_head;
|
struct rcu_head rcu_head;
|
||||||
|
|
||||||
|
#ifdef CONFIG_DEBUG_FS
|
||||||
|
struct dentry *dentry;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -113,7 +126,14 @@ struct device_list_opp {
|
||||||
* @dev_list: list of devices that share these OPPs
|
* @dev_list: list of devices that share these OPPs
|
||||||
* @opp_list: list of opps
|
* @opp_list: list of opps
|
||||||
* @np: struct device_node pointer for opp's DT node.
|
* @np: struct device_node pointer for opp's DT node.
|
||||||
|
* @clock_latency_ns_max: Max clock latency in nanoseconds.
|
||||||
* @shared_opp: OPP is shared between multiple devices.
|
* @shared_opp: OPP is shared between multiple devices.
|
||||||
|
* @suspend_opp: Pointer to OPP to be used during device suspend.
|
||||||
|
* @supported_hw: Array of version number to support.
|
||||||
|
* @supported_hw_count: Number of elements in supported_hw array.
|
||||||
|
* @prop_name: A name to postfix to many DT properties, while parsing them.
|
||||||
|
* @dentry: debugfs dentry pointer of the real device directory (not links).
|
||||||
|
* @dentry_name: Name of the real dentry.
|
||||||
*
|
*
|
||||||
* This is an internal data structure maintaining the link to opps attached to
|
* This is an internal data structure maintaining the link to opps attached to
|
||||||
* a device. This structure is not meant to be shared to users as it is
|
* a device. This structure is not meant to be shared to users as it is
|
||||||
|
@ -135,6 +155,15 @@ struct device_opp {
|
||||||
unsigned long clock_latency_ns_max;
|
unsigned long clock_latency_ns_max;
|
||||||
bool shared_opp;
|
bool shared_opp;
|
||||||
struct dev_pm_opp *suspend_opp;
|
struct dev_pm_opp *suspend_opp;
|
||||||
|
|
||||||
|
unsigned int *supported_hw;
|
||||||
|
unsigned int supported_hw_count;
|
||||||
|
const char *prop_name;
|
||||||
|
|
||||||
|
#ifdef CONFIG_DEBUG_FS
|
||||||
|
struct dentry *dentry;
|
||||||
|
char dentry_name[NAME_MAX];
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Routines internal to opp core */
|
/* Routines internal to opp core */
|
||||||
|
@ -143,4 +172,26 @@ struct device_list_opp *_add_list_dev(const struct device *dev,
|
||||||
struct device_opp *dev_opp);
|
struct device_opp *dev_opp);
|
||||||
struct device_node *_of_get_opp_desc_node(struct device *dev);
|
struct device_node *_of_get_opp_desc_node(struct device *dev);
|
||||||
|
|
||||||
|
#ifdef CONFIG_DEBUG_FS
|
||||||
|
void opp_debug_remove_one(struct dev_pm_opp *opp);
|
||||||
|
int opp_debug_create_one(struct dev_pm_opp *opp, struct device_opp *dev_opp);
|
||||||
|
int opp_debug_register(struct device_list_opp *list_dev,
|
||||||
|
struct device_opp *dev_opp);
|
||||||
|
void opp_debug_unregister(struct device_list_opp *list_dev,
|
||||||
|
struct device_opp *dev_opp);
|
||||||
|
#else
|
||||||
|
static inline void opp_debug_remove_one(struct dev_pm_opp *opp) {}
|
||||||
|
|
||||||
|
static inline int opp_debug_create_one(struct dev_pm_opp *opp,
|
||||||
|
struct device_opp *dev_opp)
|
||||||
|
{ return 0; }
|
||||||
|
static inline int opp_debug_register(struct device_list_opp *list_dev,
|
||||||
|
struct device_opp *dev_opp)
|
||||||
|
{ return 0; }
|
||||||
|
|
||||||
|
static inline void opp_debug_unregister(struct device_list_opp *list_dev,
|
||||||
|
struct device_opp *dev_opp)
|
||||||
|
{ }
|
||||||
|
#endif /* DEBUG_FS */
|
||||||
|
|
||||||
#endif /* __DRIVER_OPP_H__ */
|
#endif /* __DRIVER_OPP_H__ */
|
||||||
|
|
|
@ -55,6 +55,11 @@ int dev_pm_opp_enable(struct device *dev, unsigned long freq);
|
||||||
int dev_pm_opp_disable(struct device *dev, unsigned long freq);
|
int dev_pm_opp_disable(struct device *dev, unsigned long freq);
|
||||||
|
|
||||||
struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev);
|
struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev);
|
||||||
|
int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions,
|
||||||
|
unsigned int count);
|
||||||
|
void dev_pm_opp_put_supported_hw(struct device *dev);
|
||||||
|
int dev_pm_opp_set_prop_name(struct device *dev, const char *name);
|
||||||
|
void dev_pm_opp_put_prop_name(struct device *dev);
|
||||||
#else
|
#else
|
||||||
static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
|
static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
|
||||||
{
|
{
|
||||||
|
@ -129,6 +134,23 @@ static inline struct srcu_notifier_head *dev_pm_opp_get_notifier(
|
||||||
{
|
{
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int dev_pm_opp_set_supported_hw(struct device *dev,
|
||||||
|
const u32 *versions,
|
||||||
|
unsigned int count)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void dev_pm_opp_put_supported_hw(struct device *dev) {}
|
||||||
|
|
||||||
|
static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void dev_pm_opp_put_prop_name(struct device *dev) {}
|
||||||
|
|
||||||
#endif /* CONFIG_PM_OPP */
|
#endif /* CONFIG_PM_OPP */
|
||||||
|
|
||||||
#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)
|
#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)
|
||||||
|
|
Loading…
Reference in New Issue