mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-29 17:22:07 +00:00
Merge branches 'pm-cpufreq-sched' and 'pm-opp'
* pm-cpufreq-sched: cpufreq: schedutil: Reset cached_raw_freq when not in sync with next_freq * pm-opp: PM / OPP: Add dev_pm_opp_{un}register_get_pstate_helper() PM / OPP: Support updating performance state of device's power domain PM / OPP: add missing of_node_put() for of_get_cpu_node() PM / OPP: Rename dev_pm_opp_register_put_opp_helper() PM / OPP: Add missing of_node_put(np) PM / OPP: Move error message to debug level PM / OPP: Use snprintf() to avoid kasprintf() and kfree() PM / OPP: Move the OPP directory out of power/
This commit is contained in:
commit
28da43956b
@ -10049,7 +10049,7 @@ M: Stephen Boyd <sboyd@codeaurora.org>
|
||||
L: linux-pm@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm.git
|
||||
F: drivers/base/power/opp/
|
||||
F: drivers/opp/
|
||||
F: include/linux/pm_opp.h
|
||||
F: Documentation/power/opp.txt
|
||||
F: Documentation/devicetree/bindings/opp/
|
||||
|
@ -209,4 +209,6 @@ source "drivers/tee/Kconfig"
|
||||
|
||||
source "drivers/mux/Kconfig"
|
||||
|
||||
source "drivers/opp/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
@ -126,6 +126,7 @@ obj-$(CONFIG_ACCESSIBILITY) += accessibility/
|
||||
obj-$(CONFIG_ISDN) += isdn/
|
||||
obj-$(CONFIG_EDAC) += edac/
|
||||
obj-$(CONFIG_EISA) += eisa/
|
||||
obj-$(CONFIG_PM_OPP) += opp/
|
||||
obj-$(CONFIG_CPU_FREQ) += cpufreq/
|
||||
obj-$(CONFIG_CPU_IDLE) += cpuidle/
|
||||
obj-y += mmc/
|
||||
|
@ -2,7 +2,6 @@
|
||||
obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o wakeirq.o
|
||||
obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
|
||||
obj-$(CONFIG_PM_TRACE_RTC) += trace.o
|
||||
obj-$(CONFIG_PM_OPP) += opp/
|
||||
obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o
|
||||
obj-$(CONFIG_HAVE_CLK) += clock_ops.o
|
||||
|
||||
|
13
drivers/opp/Kconfig
Normal file
13
drivers/opp/Kconfig
Normal file
@ -0,0 +1,13 @@
|
||||
config PM_OPP
|
||||
bool
|
||||
select SRCU
|
||||
---help---
|
||||
SOCs have a standard set of tuples consisting of frequency and
|
||||
voltage pairs that the device will support per voltage domain. This
|
||||
is called Operating Performance Point or OPP. The actual definitions
|
||||
of OPP varies over silicon within the same family of devices.
|
||||
|
||||
OPP layer organizes the data internally using device pointers
|
||||
representing individual voltage domains and provides SOC
|
||||
implementations a ready to use framework to manage OPPs.
|
||||
For more information, read <file:Documentation/power/opp.txt>
|
@ -19,6 +19,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "opp.h"
|
||||
@ -296,7 +297,7 @@ int dev_pm_opp_get_opp_count(struct device *dev)
|
||||
opp_table = _find_opp_table(dev);
|
||||
if (IS_ERR(opp_table)) {
|
||||
count = PTR_ERR(opp_table);
|
||||
dev_err(dev, "%s: OPP table not found (%d)\n",
|
||||
dev_dbg(dev, "%s: OPP table not found (%d)\n",
|
||||
__func__, count);
|
||||
return count;
|
||||
}
|
||||
@ -535,6 +536,44 @@ _generic_set_opp_clk_only(struct device *dev, struct clk *clk,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
_generic_set_opp_domain(struct device *dev, struct clk *clk,
|
||||
unsigned long old_freq, unsigned long freq,
|
||||
unsigned int old_pstate, unsigned int new_pstate)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Scaling up? Scale domain performance state before frequency */
|
||||
if (freq > old_freq) {
|
||||
ret = dev_pm_genpd_set_performance_state(dev, new_pstate);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq);
|
||||
if (ret)
|
||||
goto restore_domain_state;
|
||||
|
||||
/* Scaling down? Scale domain performance state after frequency */
|
||||
if (freq < old_freq) {
|
||||
ret = dev_pm_genpd_set_performance_state(dev, new_pstate);
|
||||
if (ret)
|
||||
goto restore_freq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
restore_freq:
|
||||
if (_generic_set_opp_clk_only(dev, clk, freq, old_freq))
|
||||
dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n",
|
||||
__func__, old_freq);
|
||||
restore_domain_state:
|
||||
if (freq > old_freq)
|
||||
dev_pm_genpd_set_performance_state(dev, old_pstate);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _generic_set_opp_regulator(const struct opp_table *opp_table,
|
||||
struct device *dev,
|
||||
unsigned long old_freq,
|
||||
@ -653,7 +692,16 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
|
||||
|
||||
/* Only frequency scaling */
|
||||
if (!opp_table->regulators) {
|
||||
ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq);
|
||||
/*
|
||||
* We don't support devices with both regulator and
|
||||
* domain performance-state for now.
|
||||
*/
|
||||
if (opp_table->genpd_performance_state)
|
||||
ret = _generic_set_opp_domain(dev, clk, old_freq, freq,
|
||||
IS_ERR(old_opp) ? 0 : old_opp->pstate,
|
||||
opp->pstate);
|
||||
else
|
||||
ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq);
|
||||
} else if (!opp_table->set_opp) {
|
||||
ret = _generic_set_opp_regulator(opp_table, dev, old_freq, freq,
|
||||
IS_ERR(old_opp) ? NULL : old_opp->supplies,
|
||||
@ -988,6 +1036,9 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (opp_table->get_pstate)
|
||||
new_opp->pstate = opp_table->get_pstate(dev, new_opp->rate);
|
||||
|
||||
list_add(&new_opp->node, head);
|
||||
mutex_unlock(&opp_table->lock);
|
||||
|
||||
@ -1476,13 +1527,13 @@ struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev,
|
||||
EXPORT_SYMBOL_GPL(dev_pm_opp_register_set_opp_helper);
|
||||
|
||||
/**
|
||||
* dev_pm_opp_register_put_opp_helper() - Releases resources blocked for
|
||||
* dev_pm_opp_unregister_set_opp_helper() - Releases resources blocked for
|
||||
* set_opp helper
|
||||
* @opp_table: OPP table returned from dev_pm_opp_register_set_opp_helper().
|
||||
*
|
||||
* Release resources blocked for platform specific set_opp helper.
|
||||
*/
|
||||
void dev_pm_opp_register_put_opp_helper(struct opp_table *opp_table)
|
||||
void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table)
|
||||
{
|
||||
if (!opp_table->set_opp) {
|
||||
pr_err("%s: Doesn't have custom set_opp helper set\n",
|
||||
@ -1497,7 +1548,82 @@ void dev_pm_opp_register_put_opp_helper(struct opp_table *opp_table)
|
||||
|
||||
dev_pm_opp_put_opp_table(opp_table);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_opp_register_put_opp_helper);
|
||||
EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_set_opp_helper);
|
||||
|
||||
/**
|
||||
* dev_pm_opp_register_get_pstate_helper() - Register get_pstate() helper.
|
||||
* @dev: Device for which the helper is getting registered.
|
||||
* @get_pstate: Helper.
|
||||
*
|
||||
* TODO: Remove this callback after the same information is available via Device
|
||||
* Tree.
|
||||
*
|
||||
* This allows a platform to initialize the performance states of individual
|
||||
* OPPs for its devices, until we get similar information directly from DT.
|
||||
*
|
||||
* This must be called before the OPPs are initialized for the device.
|
||||
*/
|
||||
struct opp_table *dev_pm_opp_register_get_pstate_helper(struct device *dev,
|
||||
int (*get_pstate)(struct device *dev, unsigned long rate))
|
||||
{
|
||||
struct opp_table *opp_table;
|
||||
int ret;
|
||||
|
||||
if (!get_pstate)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
opp_table = dev_pm_opp_get_opp_table(dev);
|
||||
if (!opp_table)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* This should be called before OPPs are initialized */
|
||||
if (WARN_ON(!list_empty(&opp_table->opp_list))) {
|
||||
ret = -EBUSY;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Already have genpd_performance_state set */
|
||||
if (WARN_ON(opp_table->genpd_performance_state)) {
|
||||
ret = -EBUSY;
|
||||
goto err;
|
||||
}
|
||||
|
||||
opp_table->genpd_performance_state = true;
|
||||
opp_table->get_pstate = get_pstate;
|
||||
|
||||
return opp_table;
|
||||
|
||||
err:
|
||||
dev_pm_opp_put_opp_table(opp_table);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_opp_register_get_pstate_helper);
|
||||
|
||||
/**
|
||||
* dev_pm_opp_unregister_get_pstate_helper() - Releases resources blocked for
|
||||
* get_pstate() helper
|
||||
* @opp_table: OPP table returned from dev_pm_opp_register_get_pstate_helper().
|
||||
*
|
||||
* Release resources blocked for platform specific get_pstate() helper.
|
||||
*/
|
||||
void dev_pm_opp_unregister_get_pstate_helper(struct opp_table *opp_table)
|
||||
{
|
||||
if (!opp_table->genpd_performance_state) {
|
||||
pr_err("%s: Doesn't have performance states set\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make sure there are no concurrent readers while updating opp_table */
|
||||
WARN_ON(!list_empty(&opp_table->opp_list));
|
||||
|
||||
opp_table->genpd_performance_state = false;
|
||||
opp_table->get_pstate = NULL;
|
||||
|
||||
dev_pm_opp_put_opp_table(opp_table);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_get_pstate_helper);
|
||||
|
||||
/**
|
||||
* dev_pm_opp_add() - Add an OPP table from a table definitions
|
||||
@ -1706,6 +1832,13 @@ void _dev_pm_opp_remove_table(struct opp_table *opp_table, struct device *dev,
|
||||
if (remove_all || !opp->dynamic)
|
||||
dev_pm_opp_put(opp);
|
||||
}
|
||||
|
||||
/*
|
||||
* The OPP table is getting removed, drop the performance state
|
||||
* constraints.
|
||||
*/
|
||||
if (opp_table->genpd_performance_state)
|
||||
dev_pm_genpd_set_performance_state(dev, 0);
|
||||
} else {
|
||||
_remove_opp_dev(_find_opp_dev(dev, opp_table), opp_table);
|
||||
}
|
@ -41,16 +41,15 @@ static bool opp_debug_create_supplies(struct dev_pm_opp *opp,
|
||||
{
|
||||
struct dentry *d;
|
||||
int i;
|
||||
char *name;
|
||||
|
||||
for (i = 0; i < opp_table->regulator_count; i++) {
|
||||
name = kasprintf(GFP_KERNEL, "supply-%d", i);
|
||||
char name[15];
|
||||
|
||||
snprintf(name, sizeof(name), "supply-%d", i);
|
||||
|
||||
/* Create per-opp directory */
|
||||
d = debugfs_create_dir(name, pdentry);
|
||||
|
||||
kfree(name);
|
||||
|
||||
if (!d)
|
||||
return false;
|
||||
|
||||
@ -100,6 +99,9 @@ int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
|
||||
if (!debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!debugfs_create_u32("performance_state", S_IRUGO, d, &opp->pstate))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate))
|
||||
return -ENOMEM;
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
@ -397,6 +397,7 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
|
||||
dev_err(dev, "%s: Failed to add OPP, %d\n", __func__,
|
||||
ret);
|
||||
_dev_pm_opp_remove_table(opp_table, dev, false);
|
||||
of_node_put(np);
|
||||
goto put_opp_table;
|
||||
}
|
||||
}
|
||||
@ -603,7 +604,7 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev,
|
||||
if (cpu == cpu_dev->id)
|
||||
continue;
|
||||
|
||||
cpu_np = of_get_cpu_node(cpu, NULL);
|
||||
cpu_np = of_cpu_device_node_get(cpu);
|
||||
if (!cpu_np) {
|
||||
dev_err(cpu_dev, "%s: failed to get cpu%d node\n",
|
||||
__func__, cpu);
|
||||
@ -613,6 +614,7 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev,
|
||||
|
||||
/* Get OPP descriptor node */
|
||||
tmp_np = _opp_of_get_opp_desc_node(cpu_np);
|
||||
of_node_put(cpu_np);
|
||||
if (!tmp_np) {
|
||||
pr_err("%pOF: Couldn't find opp node\n", cpu_np);
|
||||
ret = -ENOENT;
|
@ -58,6 +58,7 @@ extern struct list_head opp_tables;
|
||||
* @dynamic: not-created from static DT entries.
|
||||
* @turbo: true if turbo (boost) OPP
|
||||
* @suspend: true if suspend OPP
|
||||
* @pstate: Device's power domain's performance state.
|
||||
* @rate: Frequency in hertz
|
||||
* @supplies: Power supplies voltage/current values
|
||||
* @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's
|
||||
@ -76,6 +77,7 @@ struct dev_pm_opp {
|
||||
bool dynamic;
|
||||
bool turbo;
|
||||
bool suspend;
|
||||
unsigned int pstate;
|
||||
unsigned long rate;
|
||||
|
||||
struct dev_pm_opp_supply *supplies;
|
||||
@ -135,8 +137,10 @@ enum opp_table_access {
|
||||
* @clk: Device's clock handle
|
||||
* @regulators: Supply regulators
|
||||
* @regulator_count: Number of power supply regulators
|
||||
* @genpd_performance_state: Device's power domain support performance state.
|
||||
* @set_opp: Platform specific set_opp callback
|
||||
* @set_opp_data: Data to be passed to set_opp callback
|
||||
* @get_pstate: Platform specific get_pstate callback
|
||||
* @dentry: debugfs dentry pointer of the real device directory (not links).
|
||||
* @dentry_name: Name of the real dentry.
|
||||
*
|
||||
@ -170,9 +174,11 @@ struct opp_table {
|
||||
struct clk *clk;
|
||||
struct regulator **regulators;
|
||||
unsigned int regulator_count;
|
||||
bool genpd_performance_state;
|
||||
|
||||
int (*set_opp)(struct dev_pm_set_opp_data *data);
|
||||
struct dev_pm_set_opp_data *set_opp_data;
|
||||
int (*get_pstate)(struct device *dev, unsigned long rate);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *dentry;
|
@ -124,7 +124,9 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table);
|
||||
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_register_put_opp_helper(struct opp_table *opp_table);
|
||||
void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table);
|
||||
struct opp_table *dev_pm_opp_register_get_pstate_helper(struct device *dev, int (*get_pstate)(struct device *dev, unsigned long rate));
|
||||
void dev_pm_opp_unregister_get_pstate_helper(struct opp_table *opp_table);
|
||||
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
|
||||
int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask);
|
||||
int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask);
|
||||
@ -243,7 +245,15 @@ static inline struct opp_table *dev_pm_opp_register_set_opp_helper(struct device
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
static inline void dev_pm_opp_register_put_opp_helper(struct opp_table *opp_table) {}
|
||||
static inline void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table) {}
|
||||
|
||||
static inline struct opp_table *dev_pm_opp_register_get_pstate_helper(struct device *dev,
|
||||
int (*get_pstate)(struct device *dev, unsigned long rate))
|
||||
{
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
static inline void dev_pm_opp_unregister_get_pstate_helper(struct opp_table *opp_table) {}
|
||||
|
||||
static inline struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name)
|
||||
{
|
||||
|
@ -259,20 +259,6 @@ config APM_EMULATION
|
||||
anything, try disabling/enabling this option (or disabling/enabling
|
||||
APM in your BIOS).
|
||||
|
||||
config PM_OPP
|
||||
bool
|
||||
select SRCU
|
||||
---help---
|
||||
SOCs have a standard set of tuples consisting of frequency and
|
||||
voltage pairs that the device will support per voltage domain. This
|
||||
is called Operating Performance Point or OPP. The actual definitions
|
||||
of OPP varies over silicon within the same family of devices.
|
||||
|
||||
OPP layer organizes the data internally using device pointers
|
||||
representing individual voltage domains and provides SOC
|
||||
implementations a ready to use framework to manage OPPs.
|
||||
For more information, read <file:Documentation/power/opp.txt>
|
||||
|
||||
config PM_CLK
|
||||
def_bool y
|
||||
depends on PM && HAVE_CLK
|
||||
|
@ -282,8 +282,12 @@ static void sugov_update_single(struct update_util_data *hook, u64 time,
|
||||
* Do not reduce the frequency if the CPU has not been idle
|
||||
* recently, as the reduction is likely to be premature then.
|
||||
*/
|
||||
if (busy && next_f < sg_policy->next_freq)
|
||||
if (busy && next_f < sg_policy->next_freq) {
|
||||
next_f = sg_policy->next_freq;
|
||||
|
||||
/* Reset cached freq as next_freq has changed */
|
||||
sg_policy->cached_raw_freq = 0;
|
||||
}
|
||||
}
|
||||
sugov_update_commit(sg_policy, time, next_f);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user