mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-27 19:29:37 +00:00
Merge branch 'pm-opp' into pm-cpufreq
This commit is contained in:
commit
68435c0d4a
7 changed files with 727 additions and 80 deletions
|
@ -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
|
||||
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
|
||||
should be documented as Documentation/devicetree/bindings/power/<vendor>-opp.txt
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
considering system specific parameters (such as transients, process, aging,
|
||||
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
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
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";
|
||||
opp-shared;
|
||||
|
||||
opp00 {
|
||||
opp@1000000000 {
|
||||
opp-hz = /bits/ 64 <1000000000>;
|
||||
opp-microvolt = <970000 975000 985000>;
|
||||
opp-microamp = <70000>;
|
||||
clock-latency-ns = <300000>;
|
||||
opp-suspend;
|
||||
};
|
||||
opp01 {
|
||||
opp@1100000000 {
|
||||
opp-hz = /bits/ 64 <1100000000>;
|
||||
opp-microvolt = <980000 1000000 1010000>;
|
||||
opp-microamp = <80000>;
|
||||
clock-latency-ns = <310000>;
|
||||
};
|
||||
opp02 {
|
||||
opp@1200000000 {
|
||||
opp-hz = /bits/ 64 <1200000000>;
|
||||
opp-microvolt = <1025000>;
|
||||
clock-latency-ns = <290000>;
|
||||
|
@ -236,20 +256,20 @@ independently.
|
|||
* independently.
|
||||
*/
|
||||
|
||||
opp00 {
|
||||
opp@1000000000 {
|
||||
opp-hz = /bits/ 64 <1000000000>;
|
||||
opp-microvolt = <970000 975000 985000>;
|
||||
opp-microamp = <70000>;
|
||||
clock-latency-ns = <300000>;
|
||||
opp-suspend;
|
||||
};
|
||||
opp01 {
|
||||
opp@1100000000 {
|
||||
opp-hz = /bits/ 64 <1100000000>;
|
||||
opp-microvolt = <980000 1000000 1010000>;
|
||||
opp-microamp = <80000>;
|
||||
clock-latency-ns = <310000>;
|
||||
};
|
||||
opp02 {
|
||||
opp@1200000000 {
|
||||
opp-hz = /bits/ 64 <1200000000>;
|
||||
opp-microvolt = <1025000>;
|
||||
opp-microamp = <90000;
|
||||
|
@ -312,20 +332,20 @@ DVFS state together.
|
|||
compatible = "operating-points-v2";
|
||||
opp-shared;
|
||||
|
||||
opp00 {
|
||||
opp@1000000000 {
|
||||
opp-hz = /bits/ 64 <1000000000>;
|
||||
opp-microvolt = <970000 975000 985000>;
|
||||
opp-microamp = <70000>;
|
||||
clock-latency-ns = <300000>;
|
||||
opp-suspend;
|
||||
};
|
||||
opp01 {
|
||||
opp@1100000000 {
|
||||
opp-hz = /bits/ 64 <1100000000>;
|
||||
opp-microvolt = <980000 1000000 1010000>;
|
||||
opp-microamp = <80000>;
|
||||
clock-latency-ns = <310000>;
|
||||
};
|
||||
opp02 {
|
||||
opp@1200000000 {
|
||||
opp-hz = /bits/ 64 <1200000000>;
|
||||
opp-microvolt = <1025000>;
|
||||
opp-microamp = <90000>;
|
||||
|
@ -338,20 +358,20 @@ DVFS state together.
|
|||
compatible = "operating-points-v2";
|
||||
opp-shared;
|
||||
|
||||
opp10 {
|
||||
opp@1300000000 {
|
||||
opp-hz = /bits/ 64 <1300000000>;
|
||||
opp-microvolt = <1045000 1050000 1055000>;
|
||||
opp-microamp = <95000>;
|
||||
clock-latency-ns = <400000>;
|
||||
opp-suspend;
|
||||
};
|
||||
opp11 {
|
||||
opp@1400000000 {
|
||||
opp-hz = /bits/ 64 <1400000000>;
|
||||
opp-microvolt = <1075000>;
|
||||
opp-microamp = <100000>;
|
||||
clock-latency-ns = <400000>;
|
||||
};
|
||||
opp12 {
|
||||
opp@1500000000 {
|
||||
opp-hz = /bits/ 64 <1500000000>;
|
||||
opp-microvolt = <1010000 1100000 1110000>;
|
||||
opp-microamp = <95000>;
|
||||
|
@ -378,7 +398,7 @@ Example 4: Handling multiple regulators
|
|||
compatible = "operating-points-v2";
|
||||
opp-shared;
|
||||
|
||||
opp00 {
|
||||
opp@1000000000 {
|
||||
opp-hz = /bits/ 64 <1000000000>;
|
||||
opp-microvolt = <970000>, /* Supply 0 */
|
||||
<960000>, /* Supply 1 */
|
||||
|
@ -391,7 +411,7 @@ Example 4: Handling multiple regulators
|
|||
|
||||
/* OR */
|
||||
|
||||
opp00 {
|
||||
opp@1000000000 {
|
||||
opp-hz = /bits/ 64 <1000000000>;
|
||||
opp-microvolt = <970000 975000 985000>, /* Supply 0 */
|
||||
<960000 965000 975000>, /* Supply 1 */
|
||||
|
@ -404,7 +424,7 @@ Example 4: Handling multiple regulators
|
|||
|
||||
/* OR */
|
||||
|
||||
opp00 {
|
||||
opp@1000000000 {
|
||||
opp-hz = /bits/ 64 <1000000000>;
|
||||
opp-microvolt = <970000 975000 985000>, /* Supply 0 */
|
||||
<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 {
|
||||
|
@ -426,40 +447,73 @@ Example 5: Multiple OPP tables
|
|||
...
|
||||
|
||||
cpu-supply = <&cpu_supply>
|
||||
operating-points-v2 = <&cpu0_opp_table_slow>, <&cpu0_opp_table_fast>;
|
||||
operating-points-names = "slow", "fast";
|
||||
operating-points-v2 = <&cpu0_opp_table_slow>;
|
||||
};
|
||||
};
|
||||
|
||||
cpu0_opp_table_slow: opp_table_slow {
|
||||
opp_table {
|
||||
compatible = "operating-points-v2";
|
||||
status = "okay";
|
||||
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-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>;
|
||||
...
|
||||
};
|
||||
};
|
||||
|
||||
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>;
|
||||
opp-microvolt = <900000 915000 925000>;
|
||||
...
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
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 @@ cpu0_opp_table: opp_table0 {
|
|||
compatible = "operating-points-v2";
|
||||
opp-shared;
|
||||
|
||||
opp00 {
|
||||
opp@200000000 {
|
||||
opp-hz = /bits/ 64 <200000000>;
|
||||
opp-microvolt = <900000>;
|
||||
clock-latency-ns = <200000>;
|
||||
};
|
||||
opp01 {
|
||||
opp@300000000 {
|
||||
opp-hz = /bits/ 64 <300000000>;
|
||||
opp-microvolt = <900000>;
|
||||
clock-latency-ns = <200000>;
|
||||
};
|
||||
opp02 {
|
||||
opp@400000000 {
|
||||
opp-hz = /bits/ 64 <400000000>;
|
||||
opp-microvolt = <925000>;
|
||||
clock-latency-ns = <200000>;
|
||||
};
|
||||
opp03 {
|
||||
opp@500000000 {
|
||||
opp-hz = /bits/ 64 <500000000>;
|
||||
opp-microvolt = <950000>;
|
||||
clock-latency-ns = <200000>;
|
||||
};
|
||||
opp04 {
|
||||
opp@600000000 {
|
||||
opp-hz = /bits/ 64 <600000000>;
|
||||
opp-microvolt = <975000>;
|
||||
clock-latency-ns = <200000>;
|
||||
};
|
||||
opp05 {
|
||||
opp@700000000 {
|
||||
opp-hz = /bits/ 64 <700000000>;
|
||||
opp-microvolt = <987500>;
|
||||
clock-latency-ns = <200000>;
|
||||
};
|
||||
opp06 {
|
||||
opp@800000000 {
|
||||
opp-hz = /bits/ 64 <800000000>;
|
||||
opp-microvolt = <1000000>;
|
||||
clock-latency-ns = <200000>;
|
||||
opp-suspend;
|
||||
};
|
||||
opp07 {
|
||||
opp@900000000 {
|
||||
opp-hz = /bits/ 64 <900000000>;
|
||||
opp-microvolt = <1037500>;
|
||||
clock-latency-ns = <200000>;
|
||||
};
|
||||
opp08 {
|
||||
opp@1000000000 {
|
||||
opp-hz = /bits/ 64 <1000000000>;
|
||||
opp-microvolt = <1087500>;
|
||||
clock-latency-ns = <200000>;
|
||||
};
|
||||
opp09 {
|
||||
opp@1100000000 {
|
||||
opp-hz = /bits/ 64 <1100000000>;
|
||||
opp-microvolt = <1137500>;
|
||||
clock-latency-ns = <200000>;
|
||||
};
|
||||
opp10 {
|
||||
opp@1200000000 {
|
||||
opp-hz = /bits/ 64 <1200000000>;
|
||||
opp-microvolt = <1187500>;
|
||||
clock-latency-ns = <200000>;
|
||||
};
|
||||
opp11 {
|
||||
opp@1300000000 {
|
||||
opp-hz = /bits/ 64 <1300000000>;
|
||||
opp-microvolt = <1250000>;
|
||||
clock-latency-ns = <200000>;
|
||||
};
|
||||
opp12 {
|
||||
opp@1400000000 {
|
||||
opp-hz = /bits/ 64 <1400000000>;
|
||||
opp-microvolt = <1287500>;
|
||||
clock-latency-ns = <200000>;
|
||||
};
|
||||
opp13 {
|
||||
opp@1500000000 {
|
||||
opp-hz = /bits/ 64 <1500000000>;
|
||||
opp-microvolt = <1350000>;
|
||||
clock-latency-ns = <200000>;
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
|
||||
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,
|
||||
struct device_opp *dev_opp)
|
||||
{
|
||||
opp_debug_unregister(list_dev, dev_opp);
|
||||
list_del(&list_dev->node);
|
||||
call_srcu(&dev_opp->srcu_head.srcu, &list_dev->rcu_head,
|
||||
_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_list_opp *list_dev;
|
||||
int ret;
|
||||
|
||||
list_dev = kzalloc(sizeof(*list_dev), GFP_KERNEL);
|
||||
if (!list_dev)
|
||||
|
@ -481,6 +483,12 @@ struct device_list_opp *_add_list_dev(const struct device *dev,
|
|||
list_dev->dev = dev;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -551,6 +559,12 @@ static void _remove_device_opp(struct device_opp *dev_opp)
|
|||
if (!list_empty(&dev_opp->opp_list))
|
||||
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,
|
||||
node);
|
||||
|
||||
|
@ -596,6 +610,7 @@ static void _opp_remove(struct device_opp *dev_opp,
|
|||
*/
|
||||
if (notify)
|
||||
srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_REMOVE, opp);
|
||||
opp_debug_remove_one(opp);
|
||||
list_del_rcu(&opp->node);
|
||||
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 list_head *head = &dev_opp->opp_list;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -776,35 +797,48 @@ static int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt,
|
|||
}
|
||||
|
||||
/* 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 val;
|
||||
int count, ret;
|
||||
struct property *prop = NULL;
|
||||
char name[NAME_MAX];
|
||||
|
||||
/* Missing property isn't a problem, but an invalid entry is */
|
||||
if (!of_find_property(opp->np, "opp-microvolt", NULL))
|
||||
return 0;
|
||||
/* Search for "opp-microvolt-<name>" */
|
||||
if (dev_opp->prop_name) {
|
||||
sprintf(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" */
|
||||
name[13] = '\0';
|
||||
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) {
|
||||
dev_err(dev, "%s: Invalid opp-microvolt property (%d)\n",
|
||||
__func__, count);
|
||||
dev_err(dev, "%s: Invalid %s property (%d)\n",
|
||||
__func__, name, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
/* There can be one or three elements here */
|
||||
if (count != 1 && count != 3) {
|
||||
dev_err(dev, "%s: Invalid number of elements in opp-microvolt property (%d)\n",
|
||||
__func__, count);
|
||||
dev_err(dev, "%s: Invalid number of elements in %s property (%d)\n",
|
||||
__func__, name, count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(opp->np, "opp-microvolt", microvolt,
|
||||
count);
|
||||
ret = of_property_read_u32_array(opp->np, name, microvolt, count);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: error parsing opp-microvolt: %d\n", __func__,
|
||||
ret);
|
||||
dev_err(dev, "%s: error parsing %s: %d\n", __func__, name, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -812,12 +846,270 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev)
|
|||
opp->u_volt_min = microvolt[1];
|
||||
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) {
|
||||
sprintf(name, "opp-microamp-%s", dev_opp->prop_name);
|
||||
prop = of_find_property(opp->np, name, NULL);
|
||||
}
|
||||
|
||||
if (!prop) {
|
||||
/* Search for "opp-microamp" */
|
||||
name[12] = '\0';
|
||||
prop = of_find_property(opp->np, name, NULL);
|
||||
}
|
||||
|
||||
if (prop && !of_property_read_u32(opp->np, name, &val))
|
||||
opp->u_amp = val;
|
||||
|
||||
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)
|
||||
* @dev: device for which we do this operation
|
||||
|
@ -864,6 +1156,12 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
|
|||
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
|
||||
* explicitly to its type. Must be fixed once rate is 64 bit
|
||||
|
@ -879,7 +1177,7 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
|
|||
if (!of_property_read_u32(np, "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)
|
||||
goto free_opp;
|
||||
|
||||
|
@ -889,12 +1187,14 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
|
|||
|
||||
/* OPP to select on device 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",
|
||||
__func__, dev_opp->suspend_opp->rate,
|
||||
new_opp->rate);
|
||||
else
|
||||
} else {
|
||||
new_opp->suspend = true;
|
||||
dev_opp->suspend_opp = new_opp;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_opp->clock_latency_ns > dev_opp->clock_latency_ns_max)
|
||||
|
|
219
drivers/base/power/opp/debugfs.c
Normal file
219
drivers/base/power/opp/debugfs.c
Normal file
|
@ -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/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/rculist.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.
|
||||
* IMPORTANT: the opp nodes should be maintained in increasing
|
||||
* order.
|
||||
* @dynamic: not-created from static DT entries.
|
||||
* @available: true/false - marks if this OPP as available or not
|
||||
* @dynamic: not-created from static DT entries.
|
||||
* @turbo: true if turbo (boost) OPP
|
||||
* @suspend: true if suspend OPP
|
||||
* @rate: Frequency in hertz
|
||||
* @u_volt: Target 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
|
||||
* @rcu_head: RCU callback head used for deferred freeing
|
||||
* @np: OPP's device node.
|
||||
* @dentry: debugfs dentry pointer (per opp)
|
||||
*
|
||||
* This structure stores the OPP information for a given device.
|
||||
*/
|
||||
|
@ -72,6 +75,7 @@ struct dev_pm_opp {
|
|||
bool available;
|
||||
bool dynamic;
|
||||
bool turbo;
|
||||
bool suspend;
|
||||
unsigned long rate;
|
||||
|
||||
unsigned long u_volt;
|
||||
|
@ -84,6 +88,10 @@ struct dev_pm_opp {
|
|||
struct rcu_head rcu_head;
|
||||
|
||||
struct device_node *np;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *dentry;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -91,6 +99,7 @@ struct dev_pm_opp {
|
|||
* @node: list node
|
||||
* @dev: device to which the struct object belongs
|
||||
* @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
|
||||
* managed by 'struct device_opp'.
|
||||
|
@ -99,6 +108,10 @@ struct device_list_opp {
|
|||
struct list_head node;
|
||||
const struct device *dev;
|
||||
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
|
||||
* @opp_list: list of opps
|
||||
* @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.
|
||||
* @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
|
||||
* 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;
|
||||
bool shared_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 */
|
||||
|
@ -143,4 +172,26 @@ struct device_list_opp *_add_list_dev(const struct device *dev,
|
|||
struct device_opp *dev_opp);
|
||||
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__ */
|
||||
|
|
|
@ -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);
|
||||
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
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 */
|
||||
|
||||
#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)
|
||||
|
|
Loading…
Reference in a new issue