Merge branch 'opp/linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm
Pull operating performance points (OPP) framework changes for 5.4 from Viresh Kumar: "This contains: - OPP core fixes to better support genpd devices (Viresh Kumar). - OPP core changes to support multiple suspend-opps in DT (Anson Huang). - New OPP API (dev_pm_opp_find_level_exact()) and Qcom OPP binding changes for CPR (Niklas Cassel). - Qcom minor update (Sricharan R). - OPP Documentation fix (Yue Hu). - OPP core support to enable/disable regulators (k.konieczny)." * 'opp/linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm: dt-bindings: opp: Add qcom-opp bindings with properties needed for CPR dt-bindings: opp: qcom-nvmem: Support pstates provided by a power domain dt-bindings: opp: qcom-nvmem: Make speedbin related properties optional dt-bindings: opp: Re-organise kryo cpufreq to use it for other nvmem based qcom socs PM / OPP: Correct Documentation about library location opp: of: Support multiple suspend OPPs defined in DT dt-bindings: opp: Support multiple opp-suspend properties opp: core: add regulators enable and disable opp: Don't decrement uninitialized list_kref opp: Add dev_pm_opp_find_level_exact() opp: Return genpd virtual devices from dev_pm_opp_attach_genpd() opp: Not all power-domains are scalable
This commit is contained in:
commit
e47bc756ad
|
@ -140,8 +140,8 @@ Optional properties:
|
|||
frequency for a short duration of time limited by the device's power, current
|
||||
and thermal limits.
|
||||
|
||||
- opp-suspend: Marks the OPP to be used during device suspend. Only one OPP in
|
||||
the table should have this.
|
||||
- opp-suspend: Marks the OPP to be used during device suspend. If multiple OPPs
|
||||
in the table have this, the OPP with highest opp-hz will be used.
|
||||
|
||||
- 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
|
||||
|
|
|
@ -1,25 +1,38 @@
|
|||
Qualcomm Technologies, Inc. KRYO CPUFreq and OPP bindings
|
||||
Qualcomm Technologies, Inc. NVMEM CPUFreq and OPP bindings
|
||||
===================================
|
||||
|
||||
In Certain Qualcomm Technologies, Inc. SoCs like apq8096 and msm8996
|
||||
that have KRYO processors, the CPU ferequencies subset and voltage value
|
||||
of each OPP varies based on the silicon variant in use.
|
||||
In Certain Qualcomm Technologies, Inc. SoCs like apq8096 and msm8996,
|
||||
the CPU frequencies subset and voltage value of each OPP varies based on
|
||||
the silicon variant in use.
|
||||
Qualcomm Technologies, Inc. Process Voltage Scaling Tables
|
||||
defines the voltage and frequency value based on the msm-id in SMEM
|
||||
and speedbin blown in the efuse combination.
|
||||
The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC
|
||||
The qcom-cpufreq-nvmem driver reads the msm-id and efuse value from the SoC
|
||||
to provide the OPP framework with required information (existing HW bitmap).
|
||||
This is used to determine the voltage and frequency value for each OPP of
|
||||
operating-points-v2 table when it is parsed by the OPP framework.
|
||||
|
||||
Required properties:
|
||||
--------------------
|
||||
In 'cpus' nodes:
|
||||
In 'cpu' nodes:
|
||||
- operating-points-v2: Phandle to the operating-points-v2 table to use.
|
||||
|
||||
In 'operating-points-v2' table:
|
||||
- compatible: Should be
|
||||
- 'operating-points-v2-kryo-cpu' for apq8096 and msm8996.
|
||||
|
||||
Optional properties:
|
||||
--------------------
|
||||
In 'cpu' nodes:
|
||||
- power-domains: A phandle pointing to the PM domain specifier which provides
|
||||
the performance states available for active state management.
|
||||
Please refer to the power-domains bindings
|
||||
Documentation/devicetree/bindings/power/power_domain.txt
|
||||
and also examples below.
|
||||
- power-domain-names: Should be
|
||||
- 'cpr' for qcs404.
|
||||
|
||||
In 'operating-points-v2' table:
|
||||
- nvmem-cells: A phandle pointing to a nvmem-cells node representing the
|
||||
efuse registers that has information about the
|
||||
speedbin that is used to select the right frequency/voltage
|
||||
|
@ -678,3 +691,105 @@ soc {
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
Example 2:
|
||||
---------
|
||||
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
CPU0: cpu@100 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a53";
|
||||
reg = <0x100>;
|
||||
....
|
||||
clocks = <&apcs_glb>;
|
||||
operating-points-v2 = <&cpu_opp_table>;
|
||||
power-domains = <&cpr>;
|
||||
power-domain-names = "cpr";
|
||||
};
|
||||
|
||||
CPU1: cpu@101 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a53";
|
||||
reg = <0x101>;
|
||||
....
|
||||
clocks = <&apcs_glb>;
|
||||
operating-points-v2 = <&cpu_opp_table>;
|
||||
power-domains = <&cpr>;
|
||||
power-domain-names = "cpr";
|
||||
};
|
||||
|
||||
CPU2: cpu@102 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a53";
|
||||
reg = <0x102>;
|
||||
....
|
||||
clocks = <&apcs_glb>;
|
||||
operating-points-v2 = <&cpu_opp_table>;
|
||||
power-domains = <&cpr>;
|
||||
power-domain-names = "cpr";
|
||||
};
|
||||
|
||||
CPU3: cpu@103 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a53";
|
||||
reg = <0x103>;
|
||||
....
|
||||
clocks = <&apcs_glb>;
|
||||
operating-points-v2 = <&cpu_opp_table>;
|
||||
power-domains = <&cpr>;
|
||||
power-domain-names = "cpr";
|
||||
};
|
||||
};
|
||||
|
||||
cpu_opp_table: cpu-opp-table {
|
||||
compatible = "operating-points-v2-kryo-cpu";
|
||||
opp-shared;
|
||||
|
||||
opp-1094400000 {
|
||||
opp-hz = /bits/ 64 <1094400000>;
|
||||
required-opps = <&cpr_opp1>;
|
||||
};
|
||||
opp-1248000000 {
|
||||
opp-hz = /bits/ 64 <1248000000>;
|
||||
required-opps = <&cpr_opp2>;
|
||||
};
|
||||
opp-1401600000 {
|
||||
opp-hz = /bits/ 64 <1401600000>;
|
||||
required-opps = <&cpr_opp3>;
|
||||
};
|
||||
};
|
||||
|
||||
cpr_opp_table: cpr-opp-table {
|
||||
compatible = "operating-points-v2-qcom-level";
|
||||
|
||||
cpr_opp1: opp1 {
|
||||
opp-level = <1>;
|
||||
qcom,opp-fuse-level = <1>;
|
||||
};
|
||||
cpr_opp2: opp2 {
|
||||
opp-level = <2>;
|
||||
qcom,opp-fuse-level = <2>;
|
||||
};
|
||||
cpr_opp3: opp3 {
|
||||
opp-level = <3>;
|
||||
qcom,opp-fuse-level = <3>;
|
||||
};
|
||||
};
|
||||
|
||||
....
|
||||
|
||||
soc {
|
||||
....
|
||||
cpr: power-controller@b018000 {
|
||||
compatible = "qcom,qcs404-cpr", "qcom,cpr";
|
||||
reg = <0x0b018000 0x1000>;
|
||||
....
|
||||
vdd-apc-supply = <&pms405_s3>;
|
||||
#power-domain-cells = <0>;
|
||||
operating-points-v2 = <&cpr_opp_table>;
|
||||
....
|
||||
};
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
Qualcomm OPP bindings to describe OPP nodes
|
||||
|
||||
The bindings are based on top of the operating-points-v2 bindings
|
||||
described in Documentation/devicetree/bindings/opp/opp.txt
|
||||
Additional properties are described below.
|
||||
|
||||
* OPP Table Node
|
||||
|
||||
Required properties:
|
||||
- compatible: Allow OPPs to express their compatibility. It should be:
|
||||
"operating-points-v2-qcom-level"
|
||||
|
||||
* OPP Node
|
||||
|
||||
Required properties:
|
||||
- qcom,opp-fuse-level: A positive value representing the fuse corner/level
|
||||
associated with this OPP node. Sometimes several corners/levels shares
|
||||
a certain fuse corner/level. A fuse corner/level contains e.g. ref uV,
|
||||
min uV, and max uV.
|
|
@ -46,7 +46,7 @@ We can represent these as three OPPs as the following {Hz, uV} tuples:
|
|||
----------------------------------------
|
||||
|
||||
OPP library provides a set of helper functions to organize and query the OPP
|
||||
information. The library is located in drivers/base/power/opp.c and the header
|
||||
information. The library is located in drivers/opp/ directory and the header
|
||||
is located in include/linux/pm_opp.h. OPP library can be enabled by enabling
|
||||
CONFIG_PM_OPP from power management menuconfig menu. OPP library depends on
|
||||
CONFIG_PM as certain SoCs such as Texas Instrument's OMAP framework allows to
|
||||
|
|
|
@ -401,6 +401,54 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact);
|
||||
|
||||
/**
|
||||
* dev_pm_opp_find_level_exact() - search for an exact level
|
||||
* @dev: device for which we do this operation
|
||||
* @level: level to search for
|
||||
*
|
||||
* Return: Searches for exact match in the opp table and returns pointer to the
|
||||
* matching opp if found, else returns ERR_PTR in case of error and should
|
||||
* be handled using IS_ERR. Error return values can be:
|
||||
* EINVAL: for bad pointer
|
||||
* ERANGE: no match found for search
|
||||
* ENODEV: if device not found in list of registered devices
|
||||
*
|
||||
* The callers are required to call dev_pm_opp_put() for the returned OPP after
|
||||
* use.
|
||||
*/
|
||||
struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev,
|
||||
unsigned int level)
|
||||
{
|
||||
struct opp_table *opp_table;
|
||||
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
|
||||
|
||||
opp_table = _find_opp_table(dev);
|
||||
if (IS_ERR(opp_table)) {
|
||||
int r = PTR_ERR(opp_table);
|
||||
|
||||
dev_err(dev, "%s: OPP table not found (%d)\n", __func__, r);
|
||||
return ERR_PTR(r);
|
||||
}
|
||||
|
||||
mutex_lock(&opp_table->lock);
|
||||
|
||||
list_for_each_entry(temp_opp, &opp_table->opp_list, node) {
|
||||
if (temp_opp->level == level) {
|
||||
opp = temp_opp;
|
||||
|
||||
/* Increment the reference count of OPP */
|
||||
dev_pm_opp_get(opp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&opp_table->lock);
|
||||
dev_pm_opp_put_opp_table(opp_table);
|
||||
|
||||
return opp;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_opp_find_level_exact);
|
||||
|
||||
static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table,
|
||||
unsigned long *freq)
|
||||
{
|
||||
|
@ -940,6 +988,7 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index)
|
|||
BLOCKING_INIT_NOTIFIER_HEAD(&opp_table->head);
|
||||
INIT_LIST_HEAD(&opp_table->opp_list);
|
||||
kref_init(&opp_table->kref);
|
||||
kref_init(&opp_table->list_kref);
|
||||
|
||||
/* Secure the device table modification */
|
||||
list_add(&opp_table->node, &opp_tables);
|
||||
|
@ -1577,6 +1626,12 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev,
|
|||
goto free_regulators;
|
||||
}
|
||||
|
||||
ret = regulator_enable(reg);
|
||||
if (ret < 0) {
|
||||
regulator_put(reg);
|
||||
goto free_regulators;
|
||||
}
|
||||
|
||||
opp_table->regulators[i] = reg;
|
||||
}
|
||||
|
||||
|
@ -1590,8 +1645,10 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev,
|
|||
return opp_table;
|
||||
|
||||
free_regulators:
|
||||
while (i != 0)
|
||||
regulator_put(opp_table->regulators[--i]);
|
||||
while (i--) {
|
||||
regulator_disable(opp_table->regulators[i]);
|
||||
regulator_put(opp_table->regulators[i]);
|
||||
}
|
||||
|
||||
kfree(opp_table->regulators);
|
||||
opp_table->regulators = NULL;
|
||||
|
@ -1617,8 +1674,10 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table)
|
|||
/* Make sure there are no concurrent readers while updating opp_table */
|
||||
WARN_ON(!list_empty(&opp_table->opp_list));
|
||||
|
||||
for (i = opp_table->regulator_count - 1; i >= 0; i--)
|
||||
for (i = opp_table->regulator_count - 1; i >= 0; i--) {
|
||||
regulator_disable(opp_table->regulators[i]);
|
||||
regulator_put(opp_table->regulators[i]);
|
||||
}
|
||||
|
||||
_free_set_opp_data(opp_table);
|
||||
|
||||
|
@ -1771,6 +1830,7 @@ static void _opp_detach_genpd(struct opp_table *opp_table)
|
|||
* dev_pm_opp_attach_genpd - Attach genpd(s) for the device and save virtual device pointer
|
||||
* @dev: Consumer device for which the genpd is getting attached.
|
||||
* @names: Null terminated array of pointers containing names of genpd to attach.
|
||||
* @virt_devs: Pointer to return the array of virtual devices.
|
||||
*
|
||||
* Multiple generic power domains for a device are supported with the help of
|
||||
* virtual genpd devices, which are created for each consumer device - genpd
|
||||
|
@ -1784,12 +1844,16 @@ static void _opp_detach_genpd(struct opp_table *opp_table)
|
|||
*
|
||||
* This helper needs to be called once with a list of all genpd to attach.
|
||||
* Otherwise the original device structure will be used instead by the OPP core.
|
||||
*
|
||||
* The order of entries in the names array must match the order in which
|
||||
* "required-opps" are added in DT.
|
||||
*/
|
||||
struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names)
|
||||
struct opp_table *dev_pm_opp_attach_genpd(struct device *dev,
|
||||
const char **names, struct device ***virt_devs)
|
||||
{
|
||||
struct opp_table *opp_table;
|
||||
struct device *virt_dev;
|
||||
int index, ret = -EINVAL;
|
||||
int index = 0, ret = -EINVAL;
|
||||
const char **name = names;
|
||||
|
||||
opp_table = dev_pm_opp_get_opp_table(dev);
|
||||
|
@ -1815,14 +1879,6 @@ struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names
|
|||
goto unlock;
|
||||
|
||||
while (*name) {
|
||||
index = of_property_match_string(dev->of_node,
|
||||
"power-domain-names", *name);
|
||||
if (index < 0) {
|
||||
dev_err(dev, "Failed to find power domain: %s (%d)\n",
|
||||
*name, index);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (index >= opp_table->required_opp_count) {
|
||||
dev_err(dev, "Index can't be greater than required-opp-count - 1, %s (%d : %d)\n",
|
||||
*name, opp_table->required_opp_count, index);
|
||||
|
@ -1843,9 +1899,12 @@ struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names
|
|||
}
|
||||
|
||||
opp_table->genpd_virt_devs[index] = virt_dev;
|
||||
index++;
|
||||
name++;
|
||||
}
|
||||
|
||||
if (virt_devs)
|
||||
*virt_devs = opp_table->genpd_virt_devs;
|
||||
mutex_unlock(&opp_table->genpd_virt_dev_lock);
|
||||
|
||||
return opp_table;
|
||||
|
|
|
@ -617,9 +617,12 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
|
|||
/* OPP to select on device suspend */
|
||||
if (of_property_read_bool(np, "opp-suspend")) {
|
||||
if (opp_table->suspend_opp) {
|
||||
dev_warn(dev, "%s: Multiple suspend OPPs found (%lu %lu)\n",
|
||||
__func__, opp_table->suspend_opp->rate,
|
||||
new_opp->rate);
|
||||
/* Pick the OPP with higher rate as suspend OPP */
|
||||
if (new_opp->rate > opp_table->suspend_opp->rate) {
|
||||
opp_table->suspend_opp->suspend = false;
|
||||
new_opp->suspend = true;
|
||||
opp_table->suspend_opp = new_opp;
|
||||
}
|
||||
} else {
|
||||
new_opp->suspend = true;
|
||||
opp_table->suspend_opp = new_opp;
|
||||
|
@ -662,8 +665,6 @@ static int _of_add_opp_table_v2(struct device *dev, struct opp_table *opp_table)
|
|||
return 0;
|
||||
}
|
||||
|
||||
kref_init(&opp_table->list_kref);
|
||||
|
||||
/* We have opp-table node now, iterate over it and add OPPs */
|
||||
for_each_available_child_of_node(opp_table->np, np) {
|
||||
opp = _opp_add_static_v2(opp_table, dev, np);
|
||||
|
@ -672,17 +673,15 @@ static int _of_add_opp_table_v2(struct device *dev, struct opp_table *opp_table)
|
|||
dev_err(dev, "%s: Failed to add OPP, %d\n", __func__,
|
||||
ret);
|
||||
of_node_put(np);
|
||||
goto put_list_kref;
|
||||
return ret;
|
||||
} else if (opp) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* There should be one of more OPP defined */
|
||||
if (WARN_ON(!count)) {
|
||||
ret = -ENOENT;
|
||||
goto put_list_kref;
|
||||
}
|
||||
if (WARN_ON(!count))
|
||||
return -ENOENT;
|
||||
|
||||
list_for_each_entry(opp, &opp_table->opp_list, node)
|
||||
pstate_count += !!opp->pstate;
|
||||
|
@ -691,8 +690,7 @@ static int _of_add_opp_table_v2(struct device *dev, struct opp_table *opp_table)
|
|||
if (pstate_count && pstate_count != count) {
|
||||
dev_err(dev, "Not all nodes have performance state set (%d: %d)\n",
|
||||
count, pstate_count);
|
||||
ret = -ENOENT;
|
||||
goto put_list_kref;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (pstate_count)
|
||||
|
@ -701,11 +699,6 @@ static int _of_add_opp_table_v2(struct device *dev, struct opp_table *opp_table)
|
|||
opp_table->parsed_static_opps = true;
|
||||
|
||||
return 0;
|
||||
|
||||
put_list_kref:
|
||||
_put_opp_list_kref(opp_table);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Initializes OPP tables based on old-deprecated bindings */
|
||||
|
@ -731,8 +724,6 @@ static int _of_add_opp_table_v1(struct device *dev, struct opp_table *opp_table)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
kref_init(&opp_table->list_kref);
|
||||
|
||||
val = prop->value;
|
||||
while (nr) {
|
||||
unsigned long freq = be32_to_cpup(val++) * 1000;
|
||||
|
@ -742,7 +733,6 @@ static int _of_add_opp_table_v1(struct device *dev, struct opp_table *opp_table)
|
|||
if (ret) {
|
||||
dev_err(dev, "%s: Failed to add OPP %ld (%d)\n",
|
||||
__func__, freq, ret);
|
||||
_put_opp_list_kref(opp_table);
|
||||
return ret;
|
||||
}
|
||||
nr -= 2;
|
||||
|
|
|
@ -96,6 +96,8 @@ unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev);
|
|||
struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
|
||||
unsigned long freq,
|
||||
bool available);
|
||||
struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev,
|
||||
unsigned int level);
|
||||
|
||||
struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
|
||||
unsigned long *freq);
|
||||
|
@ -128,7 +130,7 @@ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name);
|
|||
void dev_pm_opp_put_clkname(struct opp_table *opp_table);
|
||||
struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data));
|
||||
void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table);
|
||||
struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names);
|
||||
struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs);
|
||||
void dev_pm_opp_detach_genpd(struct opp_table *opp_table);
|
||||
int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate);
|
||||
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
|
||||
|
@ -200,6 +202,12 @@ static inline struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
|
|||
return ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
static inline struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev,
|
||||
unsigned int level)
|
||||
{
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
static inline struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
|
||||
unsigned long *freq)
|
||||
{
|
||||
|
@ -292,7 +300,7 @@ static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const
|
|||
|
||||
static inline void dev_pm_opp_put_clkname(struct opp_table *opp_table) {}
|
||||
|
||||
static inline struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names)
|
||||
static inline struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs)
|
||||
{
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue