Skip to content

Commit

Permalink
Merge branches 'pm-cpufreq-sched' and 'pm-opp'
Browse files Browse the repository at this point in the history
* 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/
  • Loading branch information
Rafael J. Wysocki committed Nov 13, 2017
3 parents 60af981 + 07458f6 + b6aa983 commit 28da439
Show file tree
Hide file tree
Showing 14 changed files with 188 additions and 30 deletions.
2 changes: 1 addition & 1 deletion MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Expand Down
2 changes: 2 additions & 0 deletions drivers/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,6 @@ source "drivers/tee/Kconfig"

source "drivers/mux/Kconfig"

source "drivers/opp/Kconfig"

endmenu
1 change: 1 addition & 0 deletions drivers/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Expand Down
1 change: 0 additions & 1 deletion drivers/base/power/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
13 changes: 13 additions & 0 deletions drivers/opp/Kconfig
Original file line number Diff line number Diff line change
@@ -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>
File renamed without changes.
143 changes: 138 additions & 5 deletions drivers/base/power/opp/core.c → drivers/opp/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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",
Expand All @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down
File renamed without changes.
10 changes: 6 additions & 4 deletions drivers/base/power/opp/debugfs.c → drivers/opp/debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down
6 changes: 4 additions & 2 deletions drivers/base/power/opp/of.c → drivers/opp/of.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions drivers/base/power/opp/opp.h → drivers/opp/opp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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;
Expand Down
14 changes: 12 additions & 2 deletions include/linux/pm_opp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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)
{
Expand Down
Loading

0 comments on commit 28da439

Please sign in to comment.