|
|
|
@@ -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;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -98,6 +101,54 @@ static unsigned long find_available_max_freq(struct devfreq *devfreq)
|
|
|
|
|
return max_freq;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* get_freq_range() - Get the current freq range
|
|
|
|
|
* @devfreq: the devfreq instance
|
|
|
|
|
* @min_freq: the min frequency
|
|
|
|
|
* @max_freq: the max frequency
|
|
|
|
|
*
|
|
|
|
|
* This takes into consideration all constraints.
|
|
|
|
|
*/
|
|
|
|
|
static void get_freq_range(struct devfreq *devfreq,
|
|
|
|
|
unsigned long *min_freq,
|
|
|
|
|
unsigned long *max_freq)
|
|
|
|
|
{
|
|
|
|
|
unsigned long *freq_table = devfreq->profile->freq_table;
|
|
|
|
|
s32 qos_min_freq, qos_max_freq;
|
|
|
|
|
|
|
|
|
|
lockdep_assert_held(&devfreq->lock);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Initialize minimum/maximum frequency from freq table.
|
|
|
|
|
* The devfreq drivers can initialize this in either ascending or
|
|
|
|
|
* descending order and devfreq core supports both.
|
|
|
|
|
*/
|
|
|
|
|
if (freq_table[0] < freq_table[devfreq->profile->max_state - 1]) {
|
|
|
|
|
*min_freq = freq_table[0];
|
|
|
|
|
*max_freq = freq_table[devfreq->profile->max_state - 1];
|
|
|
|
|
} else {
|
|
|
|
|
*min_freq = freq_table[devfreq->profile->max_state - 1];
|
|
|
|
|
*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 OPP interface */
|
|
|
|
|
*min_freq = max(*min_freq, devfreq->scaling_min_freq);
|
|
|
|
|
*max_freq = min(*max_freq, devfreq->scaling_max_freq);
|
|
|
|
|
|
|
|
|
|
if (*min_freq > *max_freq)
|
|
|
|
|
*min_freq = *max_freq;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* devfreq_get_freq_level() - Lookup freq_table for the frequency
|
|
|
|
|
* @devfreq: the devfreq instance
|
|
|
|
@@ -351,16 +402,7 @@ int update_devfreq(struct devfreq *devfreq)
|
|
|
|
|
err = devfreq->governor->get_target_freq(devfreq, &freq);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Adjust the frequency with user freq, QoS and available freq.
|
|
|
|
|
*
|
|
|
|
|
* List from the highest priority
|
|
|
|
|
* max_freq
|
|
|
|
|
* min_freq
|
|
|
|
|
*/
|
|
|
|
|
max_freq = min(devfreq->scaling_max_freq, devfreq->max_freq);
|
|
|
|
|
min_freq = max(devfreq->scaling_min_freq, devfreq->min_freq);
|
|
|
|
|
get_freq_range(devfreq, &min_freq, &max_freq);
|
|
|
|
|
|
|
|
|
|
if (freq < min_freq) {
|
|
|
|
|
freq = min_freq;
|
|
|
|
@@ -568,26 +610,69 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
|
|
|
|
|
void *devp)
|
|
|
|
|
{
|
|
|
|
|
struct devfreq *devfreq = container_of(nb, struct devfreq, nb);
|
|
|
|
|
int ret;
|
|
|
|
|
int err = -EINVAL;
|
|
|
|
|
|
|
|
|
|
mutex_lock(&devfreq->lock);
|
|
|
|
|
|
|
|
|
|
devfreq->scaling_min_freq = find_available_min_freq(devfreq);
|
|
|
|
|
if (!devfreq->scaling_min_freq) {
|
|
|
|
|
mutex_unlock(&devfreq->lock);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
if (!devfreq->scaling_min_freq)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
devfreq->scaling_max_freq = find_available_max_freq(devfreq);
|
|
|
|
|
if (!devfreq->scaling_max_freq) {
|
|
|
|
|
mutex_unlock(&devfreq->lock);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
devfreq->scaling_max_freq = ULONG_MAX;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = update_devfreq(devfreq);
|
|
|
|
|
mutex_unlock(&devfreq->lock);
|
|
|
|
|
err = update_devfreq(devfreq);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
out:
|
|
|
|
|
mutex_unlock(&devfreq->lock);
|
|
|
|
|
if (err)
|
|
|
|
|
dev_err(devfreq->dev.parent,
|
|
|
|
|
"failed to update frequency from OPP notifier (%d)\n",
|
|
|
|
|
err);
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@@ -599,16 +684,36 @@ 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);
|
|
|
|
|
if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) {
|
|
|
|
|
mutex_unlock(&devfreq_list_lock);
|
|
|
|
|
dev_warn(&devfreq->dev, "releasing devfreq which doesn't exist\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
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 (dev_pm_qos_request_active(&devfreq->user_max_freq_req)) {
|
|
|
|
|
err = dev_pm_qos_remove_request(&devfreq->user_max_freq_req);
|
|
|
|
|
if (err)
|
|
|
|
|
dev_warn(dev->parent,
|
|
|
|
|
"Failed to remove max_freq request: %d\n", err);
|
|
|
|
|
}
|
|
|
|
|
if (dev_pm_qos_request_active(&devfreq->user_min_freq_req)) {
|
|
|
|
|
err = dev_pm_qos_remove_request(&devfreq->user_min_freq_req);
|
|
|
|
|
if (err)
|
|
|
|
|
dev_warn(dev->parent,
|
|
|
|
|
"Failed to remove min_freq request: %d\n", err);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (devfreq->profile->exit)
|
|
|
|
|
devfreq->profile->exit(devfreq->dev.parent);
|
|
|
|
|
|
|
|
|
@@ -660,6 +765,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
|
|
|
|
|
devfreq->dev.parent = dev;
|
|
|
|
|
devfreq->dev.class = devfreq_class;
|
|
|
|
|
devfreq->dev.release = devfreq_dev_release;
|
|
|
|
|
INIT_LIST_HEAD(&devfreq->node);
|
|
|
|
|
devfreq->profile = profile;
|
|
|
|
|
strncpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN);
|
|
|
|
|
devfreq->previous_freq = profile->initial_freq;
|
|
|
|
@@ -681,7 +787,6 @@ struct devfreq *devfreq_add_device(struct device *dev,
|
|
|
|
|
err = -EINVAL;
|
|
|
|
|
goto err_dev;
|
|
|
|
|
}
|
|
|
|
|
devfreq->min_freq = devfreq->scaling_min_freq;
|
|
|
|
|
|
|
|
|
|
devfreq->scaling_max_freq = find_available_max_freq(devfreq);
|
|
|
|
|
if (!devfreq->scaling_max_freq) {
|
|
|
|
@@ -689,7 +794,6 @@ struct devfreq *devfreq_add_device(struct device *dev,
|
|
|
|
|
err = -EINVAL;
|
|
|
|
|
goto err_dev;
|
|
|
|
|
}
|
|
|
|
|
devfreq->max_freq = devfreq->scaling_max_freq;
|
|
|
|
|
|
|
|
|
|
devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev);
|
|
|
|
|
atomic_set(&devfreq->suspend_count, 0);
|
|
|
|
@@ -730,6 +834,28 @@ struct devfreq *devfreq_add_device(struct device *dev,
|
|
|
|
|
|
|
|
|
|
mutex_unlock(&devfreq->lock);
|
|
|
|
|
|
|
|
|
|
err = dev_pm_qos_add_request(dev, &devfreq->user_min_freq_req,
|
|
|
|
|
DEV_PM_QOS_MIN_FREQUENCY, 0);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto err_devfreq;
|
|
|
|
|
err = dev_pm_qos_add_request(dev, &devfreq->user_max_freq_req,
|
|
|
|
|
DEV_PM_QOS_MAX_FREQUENCY,
|
|
|
|
|
PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto err_devfreq;
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
@@ -1303,41 +1429,37 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
|
|
|
|
|
unsigned long value;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Protect against theoretical sysfs writes between
|
|
|
|
|
* device_add and dev_pm_qos_add_request
|
|
|
|
|
*/
|
|
|
|
|
if (!dev_pm_qos_request_active(&df->user_min_freq_req))
|
|
|
|
|
return -EAGAIN;
|
|
|
|
|
|
|
|
|
|
ret = sscanf(buf, "%lu", &value);
|
|
|
|
|
if (ret != 1)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
mutex_lock(&df->lock);
|
|
|
|
|
/* Round down to kHz for PM QoS */
|
|
|
|
|
ret = dev_pm_qos_update_request(&df->user_min_freq_req,
|
|
|
|
|
value / HZ_PER_KHZ);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
|
if (value > df->max_freq) {
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
goto unlock;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
unsigned long *freq_table = df->profile->freq_table;
|
|
|
|
|
|
|
|
|
|
/* Get minimum frequency according to sorting order */
|
|
|
|
|
if (freq_table[0] < freq_table[df->profile->max_state - 1])
|
|
|
|
|
value = freq_table[0];
|
|
|
|
|
else
|
|
|
|
|
value = freq_table[df->profile->max_state - 1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
df->min_freq = value;
|
|
|
|
|
update_devfreq(df);
|
|
|
|
|
ret = count;
|
|
|
|
|
unlock:
|
|
|
|
|
mutex_unlock(&df->lock);
|
|
|
|
|
return ret;
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t min_freq_show(struct device *dev, struct device_attribute *attr,
|
|
|
|
|
char *buf)
|
|
|
|
|
{
|
|
|
|
|
struct devfreq *df = to_devfreq(dev);
|
|
|
|
|
unsigned long min_freq, max_freq;
|
|
|
|
|
|
|
|
|
|
return sprintf(buf, "%lu\n", max(df->scaling_min_freq, df->min_freq));
|
|
|
|
|
mutex_lock(&df->lock);
|
|
|
|
|
get_freq_range(df, &min_freq, &max_freq);
|
|
|
|
|
mutex_unlock(&df->lock);
|
|
|
|
|
|
|
|
|
|
return sprintf(buf, "%lu\n", min_freq);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
|
|
|
|
@@ -1347,33 +1469,37 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
|
|
|
|
|
unsigned long value;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Protect against theoretical sysfs writes between
|
|
|
|
|
* device_add and dev_pm_qos_add_request
|
|
|
|
|
*/
|
|
|
|
|
if (!dev_pm_qos_request_active(&df->user_max_freq_req))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
ret = sscanf(buf, "%lu", &value);
|
|
|
|
|
if (ret != 1)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
mutex_lock(&df->lock);
|
|
|
|
|
/*
|
|
|
|
|
* PM QoS frequencies are in kHz so we need to convert. Convert by
|
|
|
|
|
* rounding upwards so that the acceptable interval never shrinks.
|
|
|
|
|
*
|
|
|
|
|
* For example if the user writes "666666666" to sysfs this value will
|
|
|
|
|
* be converted to 666667 kHz and back to 666667000 Hz before an OPP
|
|
|
|
|
* lookup, this ensures that an OPP of 666666666Hz is still accepted.
|
|
|
|
|
*
|
|
|
|
|
* A value of zero means "no limit".
|
|
|
|
|
*/
|
|
|
|
|
if (value)
|
|
|
|
|
value = DIV_ROUND_UP(value, HZ_PER_KHZ);
|
|
|
|
|
else
|
|
|
|
|
value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE;
|
|
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
|
if (value < df->min_freq) {
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
goto unlock;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
unsigned long *freq_table = df->profile->freq_table;
|
|
|
|
|
ret = dev_pm_qos_update_request(&df->user_max_freq_req, value);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* Get maximum frequency according to sorting order */
|
|
|
|
|
if (freq_table[0] < freq_table[df->profile->max_state - 1])
|
|
|
|
|
value = freq_table[df->profile->max_state - 1];
|
|
|
|
|
else
|
|
|
|
|
value = freq_table[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
df->max_freq = value;
|
|
|
|
|
update_devfreq(df);
|
|
|
|
|
ret = count;
|
|
|
|
|
unlock:
|
|
|
|
|
mutex_unlock(&df->lock);
|
|
|
|
|
return ret;
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
static DEVICE_ATTR_RW(min_freq);
|
|
|
|
|
|
|
|
|
@@ -1381,8 +1507,13 @@ static ssize_t max_freq_show(struct device *dev, struct device_attribute *attr,
|
|
|
|
|
char *buf)
|
|
|
|
|
{
|
|
|
|
|
struct devfreq *df = to_devfreq(dev);
|
|
|
|
|
unsigned long min_freq, max_freq;
|
|
|
|
|
|
|
|
|
|
return sprintf(buf, "%lu\n", min(df->scaling_max_freq, df->max_freq));
|
|
|
|
|
mutex_lock(&df->lock);
|
|
|
|
|
get_freq_range(df, &min_freq, &max_freq);
|
|
|
|
|
mutex_unlock(&df->lock);
|
|
|
|
|
|
|
|
|
|
return sprintf(buf, "%lu\n", max_freq);
|
|
|
|
|
}
|
|
|
|
|
static DEVICE_ATTR_RW(max_freq);
|
|
|
|
|
|
|
|
|
|