mirror of
https://github.com/torvalds/linux.git
synced 2024-12-03 09:31:26 +00:00
Update devfreq for 5.5-rc2
Detailed description for this pull request: 1. Update devfreq core - Add PM QoS support for devfreq device with following QoS type. External user of devfreq device can request the minimum and maximum frequency according to their multiple requirements. : DEV_PM_QOS_MIN_FREQUENCY is used for requesting the minimum device frequency. : DEV_PM_QOS_MAX_FREQUENCY is used for requesting the maximum device frequency. - Use PM QoS interface when entering the min/max_freq via sysfs interface. - Add get_freq_range() helper function in order to get the final min/max frequency among the multiple requirements of min/max frequency. - Fix the issue such as fixing the return value and modify code for more correct exception handling if error happen. -----BEGIN PGP SIGNATURE----- iQJKBAABCgA0FiEEsSpuqBtbWtRe4rLGnM3fLN7rz1MFAl3t3psWHGN3MDAuY2hv aUBzYW1zdW5nLmNvbQAKCRCczd8s3uvPU/KVD/4wcNmkE9wdOvAOBuQmC1hgBu8M LkEJNa+BhJVjYh/WuEXfOKh3eJDt2d6Pn+H2lVvMCZBfRZF5kIQQCPII3PcPSLSh kSJlnhLdSawsMFp2omIlns7HHuC3a4rgPI26N8z8QIJJ70WxuhUO5XK42UrlaSjV pxQi3V3QKXlfxMukYiMsPmZ/NtAORVCnjwTw6j/CiQ7vS0D0/jeI3VGRFqhhKmLJ F+iIq3hU0dY57Z8B+ncGCbWln4fmk8KDIoguSkV1UqUq+kesIGBYQinYQsbiEvvf cqelV3hF9GK7Ht5YOM2uvoGa8SeA0YJnk+cLHU6p8Ru9+eUwTisVqMdOSkHczFL1 D7NbtqWUPEMcPhiXZyn+zsQNscVGIXoH3A0QYT+d24d2tuJ8RlY1k/0KlWb6FFqs yfMCVjWSEBOuiJyN4lRaLQlg9E+UJZ0xvnQYKazA18HQkEQC4mXStcYvJ+U9+dfb p8u8ch+fYqE7JTkitwxQJbYbn2koWHyCipW08W/o9++1BQ42/rZ7+ySVR2z8dCu/ 99za+r8ablVc6hkB6JgFY8u8rYk9DIB3SeVruvoDRKz9EzbCQGqmgYafoLblBqvA D6EcQHkzK3zkHBJqf9gCdA2hGG2f9BLeNoswXcHGgdzve3l7tLWehsfKURAGcT1w VGd8KVg6XkcxOwhnKQ== =wuo7 -----END PGP SIGNATURE----- Merge tag 'devfreq-fixes-for-5.5-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux Pull devfreq updates for 5.5-rc1 from Chanwoo Choi: "Update devfreq core: - Add PM QoS support for devfreq device with following QoS type. External user of devfreq device can request the minimum and maximum frequency according to their multiple requirements. : DEV_PM_QOS_MIN_FREQUENCY is used for requesting the minimum device frequency. : DEV_PM_QOS_MAX_FREQUENCY is used for requesting the maximum device frequency. - Use PM QoS interface when entering the min/max_freq via sysfs interface. - Add get_freq_range() helper function in order to get the final min/max frequency among the multiple requirements of min/max frequency. - Fix a function return value and modify code for more correct exception handling if errors happen." * tag 'devfreq-fixes-for-5.5-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux: PM / devfreq: Use PM QoS for sysfs min/max_freq PM / devfreq: Add PM QoS support PM / devfreq: Don't fail devfreq_dev_release if not in list PM / devfreq: Introduce get_freq_range helper PM / devfreq: Set scaling_max_freq to max on OPP notifier error PM / devfreq: Fix devfreq_notifier_call returning errno
This commit is contained in:
commit
e8ad8d5108
@ -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);
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/pm_qos.h>
|
||||
|
||||
#define DEVFREQ_NAME_LEN 16
|
||||
|
||||
@ -123,8 +124,8 @@ struct devfreq_dev_profile {
|
||||
* @previous_freq: previously configured frequency value.
|
||||
* @data: Private data of the governor. The devfreq framework does not
|
||||
* touch this.
|
||||
* @min_freq: Limit minimum frequency requested by user (0: none)
|
||||
* @max_freq: Limit maximum frequency requested by user (0: none)
|
||||
* @user_min_freq_req: PM QoS minimum frequency request from user (via sysfs)
|
||||
* @user_max_freq_req: PM QoS maximum frequency request from user (via sysfs)
|
||||
* @scaling_min_freq: Limit minimum frequency requested by OPP interface
|
||||
* @scaling_max_freq: Limit maximum frequency requested by OPP interface
|
||||
* @stop_polling: devfreq polling status of a device.
|
||||
@ -136,6 +137,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.
|
||||
*
|
||||
@ -161,8 +164,8 @@ struct devfreq {
|
||||
|
||||
void *data; /* private data for governors */
|
||||
|
||||
unsigned long min_freq;
|
||||
unsigned long max_freq;
|
||||
struct dev_pm_qos_request user_min_freq_req;
|
||||
struct dev_pm_qos_request user_max_freq_req;
|
||||
unsigned long scaling_min_freq;
|
||||
unsigned long scaling_max_freq;
|
||||
bool stop_polling;
|
||||
@ -178,6 +181,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
Block a user