Commit 05d7ae15 authored by Leonard Crestez's avatar Leonard Crestez Committed by Chanwoo Choi

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: default avatarLeonard Crestez <leonard.crestez@nxp.com>
Acked-by: default avatarChanwoo Choi <cw00.choi@samsung.com>
Reviewed-by: default avatarMatthias Kaehlcke <mka@chromium.org>
Tested-by: default avatarMatthias Kaehlcke <mka@chromium.org>
Signed-off-by: default avatarChanwoo Choi <cw00.choi@samsung.com>
parent 42a6b25e
...@@ -24,11 +24,14 @@ ...@@ -24,11 +24,14 @@
#include <linux/printk.h> #include <linux/printk.h>
#include <linux/hrtimer.h> #include <linux/hrtimer.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/pm_qos.h>
#include "governor.h" #include "governor.h"
#define CREATE_TRACE_POINTS #define CREATE_TRACE_POINTS
#include <trace/events/devfreq.h> #include <trace/events/devfreq.h>
#define HZ_PER_KHZ 1000
static struct class *devfreq_class; static struct class *devfreq_class;
/* /*
...@@ -111,6 +114,7 @@ static void get_freq_range(struct devfreq *devfreq, ...@@ -111,6 +114,7 @@ static void get_freq_range(struct devfreq *devfreq,
unsigned long *max_freq) unsigned long *max_freq)
{ {
unsigned long *freq_table = devfreq->profile->freq_table; unsigned long *freq_table = devfreq->profile->freq_table;
s32 qos_min_freq, qos_max_freq;
lockdep_assert_held(&devfreq->lock); lockdep_assert_held(&devfreq->lock);
...@@ -127,6 +131,16 @@ static void get_freq_range(struct devfreq *devfreq, ...@@ -127,6 +131,16 @@ static void get_freq_range(struct devfreq *devfreq,
*max_freq = freq_table[0]; *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 */ /* Apply constraints from sysfs */
*min_freq = max(*min_freq, devfreq->min_freq); *min_freq = max(*min_freq, devfreq->min_freq);
*max_freq = min(*max_freq, devfreq->max_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, ...@@ -626,6 +640,45 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
return NOTIFY_OK; 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. * devfreq_dev_release() - Callback for struct device to release the device.
* @dev: the devfreq device * @dev: the devfreq device
...@@ -635,11 +688,23 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type, ...@@ -635,11 +688,23 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
static void devfreq_dev_release(struct device *dev) static void devfreq_dev_release(struct device *dev)
{ {
struct devfreq *devfreq = to_devfreq(dev); struct devfreq *devfreq = to_devfreq(dev);
int err;
mutex_lock(&devfreq_list_lock); mutex_lock(&devfreq_list_lock);
list_del(&devfreq->node); list_del(&devfreq->node);
mutex_unlock(&devfreq_list_lock); 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) if (devfreq->profile->exit)
devfreq->profile->exit(devfreq->dev.parent); devfreq->profile->exit(devfreq->dev.parent);
...@@ -762,6 +827,18 @@ struct devfreq *devfreq_add_device(struct device *dev, ...@@ -762,6 +827,18 @@ struct devfreq *devfreq_add_device(struct device *dev,
mutex_unlock(&devfreq->lock); 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); mutex_lock(&devfreq_list_lock);
governor = try_then_request_governor(devfreq->governor_name); governor = try_then_request_governor(devfreq->governor_name);
......
...@@ -136,6 +136,8 @@ struct devfreq_dev_profile { ...@@ -136,6 +136,8 @@ struct devfreq_dev_profile {
* @time_in_state: Statistics of devfreq states * @time_in_state: Statistics of devfreq states
* @last_stat_updated: The last time stat updated * @last_stat_updated: The last time stat updated
* @transition_notifier_list: list head of DEVFREQ_TRANSITION_NOTIFIER notifier * @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. * This structure stores the devfreq information for a give device.
* *
...@@ -178,6 +180,9 @@ struct devfreq { ...@@ -178,6 +180,9 @@ struct devfreq {
unsigned long last_stat_updated; unsigned long last_stat_updated;
struct srcu_notifier_head transition_notifier_list; struct srcu_notifier_head transition_notifier_list;
struct notifier_block nb_min;
struct notifier_block nb_max;
}; };
struct devfreq_freqs { struct devfreq_freqs {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment