mirror of https://gitee.com/openkylin/linux.git
PM / devfreq: Add PM QoS support
Register notifiers with the PM QoS framework in order to respond to requests for DEV_PM_QOS_MIN_FREQUENCY and DEV_PM_QOS_MAX_FREQUENCY. No notifiers are added by this patch but PM QoS constraints can be imposed externally (for example from other devices). Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com> Acked-by: Chanwoo Choi <cw00.choi@samsung.com> Reviewed-by: Matthias Kaehlcke <mka@chromium.org> Tested-by: Matthias Kaehlcke <mka@chromium.org> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
This commit is contained in:
parent
42a6b25e67
commit
05d7ae15cf
|
@ -24,11 +24,14 @@
|
|||
#include <linux/printk.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include "governor.h"
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/devfreq.h>
|
||||
|
||||
#define HZ_PER_KHZ 1000
|
||||
|
||||
static struct class *devfreq_class;
|
||||
|
||||
/*
|
||||
|
@ -111,6 +114,7 @@ static void get_freq_range(struct devfreq *devfreq,
|
|||
unsigned long *max_freq)
|
||||
{
|
||||
unsigned long *freq_table = devfreq->profile->freq_table;
|
||||
s32 qos_min_freq, qos_max_freq;
|
||||
|
||||
lockdep_assert_held(&devfreq->lock);
|
||||
|
||||
|
@ -127,6 +131,16 @@ static void get_freq_range(struct devfreq *devfreq,
|
|||
*max_freq = freq_table[0];
|
||||
}
|
||||
|
||||
/* Apply constraints from PM QoS */
|
||||
qos_min_freq = dev_pm_qos_read_value(devfreq->dev.parent,
|
||||
DEV_PM_QOS_MIN_FREQUENCY);
|
||||
qos_max_freq = dev_pm_qos_read_value(devfreq->dev.parent,
|
||||
DEV_PM_QOS_MAX_FREQUENCY);
|
||||
*min_freq = max(*min_freq, (unsigned long)HZ_PER_KHZ * qos_min_freq);
|
||||
if (qos_max_freq != PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE)
|
||||
*max_freq = min(*max_freq,
|
||||
(unsigned long)HZ_PER_KHZ * qos_max_freq);
|
||||
|
||||
/* Apply constraints from sysfs */
|
||||
*min_freq = max(*min_freq, devfreq->min_freq);
|
||||
*max_freq = min(*max_freq, devfreq->max_freq);
|
||||
|
@ -626,6 +640,45 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
|
|||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* qos_notifier_call() - Common handler for QoS constraints.
|
||||
* @devfreq: the devfreq instance.
|
||||
*/
|
||||
static int qos_notifier_call(struct devfreq *devfreq)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&devfreq->lock);
|
||||
err = update_devfreq(devfreq);
|
||||
mutex_unlock(&devfreq->lock);
|
||||
if (err)
|
||||
dev_err(devfreq->dev.parent,
|
||||
"failed to update frequency from PM QoS (%d)\n",
|
||||
err);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* qos_min_notifier_call() - Callback for QoS min_freq changes.
|
||||
* @nb: Should be devfreq->nb_min
|
||||
*/
|
||||
static int qos_min_notifier_call(struct notifier_block *nb,
|
||||
unsigned long val, void *ptr)
|
||||
{
|
||||
return qos_notifier_call(container_of(nb, struct devfreq, nb_min));
|
||||
}
|
||||
|
||||
/**
|
||||
* qos_max_notifier_call() - Callback for QoS max_freq changes.
|
||||
* @nb: Should be devfreq->nb_max
|
||||
*/
|
||||
static int qos_max_notifier_call(struct notifier_block *nb,
|
||||
unsigned long val, void *ptr)
|
||||
{
|
||||
return qos_notifier_call(container_of(nb, struct devfreq, nb_max));
|
||||
}
|
||||
|
||||
/**
|
||||
* devfreq_dev_release() - Callback for struct device to release the device.
|
||||
* @dev: the devfreq device
|
||||
|
@ -635,11 +688,23 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
|
|||
static void devfreq_dev_release(struct device *dev)
|
||||
{
|
||||
struct devfreq *devfreq = to_devfreq(dev);
|
||||
int err;
|
||||
|
||||
mutex_lock(&devfreq_list_lock);
|
||||
list_del(&devfreq->node);
|
||||
mutex_unlock(&devfreq_list_lock);
|
||||
|
||||
err = dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_max,
|
||||
DEV_PM_QOS_MAX_FREQUENCY);
|
||||
if (err && err != -ENOENT)
|
||||
dev_warn(dev->parent,
|
||||
"Failed to remove max_freq notifier: %d\n", err);
|
||||
err = dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_min,
|
||||
DEV_PM_QOS_MIN_FREQUENCY);
|
||||
if (err && err != -ENOENT)
|
||||
dev_warn(dev->parent,
|
||||
"Failed to remove min_freq notifier: %d\n", err);
|
||||
|
||||
if (devfreq->profile->exit)
|
||||
devfreq->profile->exit(devfreq->dev.parent);
|
||||
|
||||
|
@ -762,6 +827,18 @@ struct devfreq *devfreq_add_device(struct device *dev,
|
|||
|
||||
mutex_unlock(&devfreq->lock);
|
||||
|
||||
devfreq->nb_min.notifier_call = qos_min_notifier_call;
|
||||
err = dev_pm_qos_add_notifier(devfreq->dev.parent, &devfreq->nb_min,
|
||||
DEV_PM_QOS_MIN_FREQUENCY);
|
||||
if (err)
|
||||
goto err_devfreq;
|
||||
|
||||
devfreq->nb_max.notifier_call = qos_max_notifier_call;
|
||||
err = dev_pm_qos_add_notifier(devfreq->dev.parent, &devfreq->nb_max,
|
||||
DEV_PM_QOS_MAX_FREQUENCY);
|
||||
if (err)
|
||||
goto err_devfreq;
|
||||
|
||||
mutex_lock(&devfreq_list_lock);
|
||||
|
||||
governor = try_then_request_governor(devfreq->governor_name);
|
||||
|
|
|
@ -136,6 +136,8 @@ struct devfreq_dev_profile {
|
|||
* @time_in_state: Statistics of devfreq states
|
||||
* @last_stat_updated: The last time stat updated
|
||||
* @transition_notifier_list: list head of DEVFREQ_TRANSITION_NOTIFIER notifier
|
||||
* @nb_min: Notifier block for DEV_PM_QOS_MIN_FREQUENCY
|
||||
* @nb_max: Notifier block for DEV_PM_QOS_MAX_FREQUENCY
|
||||
*
|
||||
* This structure stores the devfreq information for a give device.
|
||||
*
|
||||
|
@ -178,6 +180,9 @@ struct devfreq {
|
|||
unsigned long last_stat_updated;
|
||||
|
||||
struct srcu_notifier_head transition_notifier_list;
|
||||
|
||||
struct notifier_block nb_min;
|
||||
struct notifier_block nb_max;
|
||||
};
|
||||
|
||||
struct devfreq_freqs {
|
||||
|
|
Loading…
Reference in New Issue