@@ -22,17 +22,20 @@
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/printk.h>
#include <linux/hrtimer.h>
#include <linux/of.h>
+#include <linux/pm_qos.h>
#include "governor.h"
#define HZ_PER_KHZ 1000
#define CREATE_TRACE_POINTS
#include <trace/events/devfreq.h>
+#define HZ_PER_KHZ 1000
+
static struct class *devfreq_class;
/*
* devfreq core provides delayed work based load monitoring helper
* functions. Governors can use these or can implement their own
@@ -123,10 +126,16 @@ static void devfreq_get_freq_range(struct devfreq *devfreq,
} else {
*min_freq = freq_table[devfreq->profile->max_state - 1];
*max_freq = freq_table[0];
}
+ /* constraints from dev_pm_qos: */
+ *min_freq = max(*min_freq, HZ_PER_KHZ * (unsigned long)dev_pm_qos_read_value(
+ devfreq->dev.parent, DEV_PM_QOS_MIN_FREQUENCY));
+ *max_freq = min(*max_freq, HZ_PER_KHZ * (unsigned long)dev_pm_qos_read_value(
+ devfreq->dev.parent, DEV_PM_QOS_MAX_FREQUENCY));
+
/* constraints from sysfs: */
*min_freq = max(*min_freq, devfreq->min_freq);
*max_freq = min(*max_freq, devfreq->max_freq);
/* constraints from opp interface: */
@@ -604,10 +613,49 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
mutex_unlock(&devfreq->lock);
return ret;
}
+/**
+ * devfreq_qos_notifier_call() - Common handler for qos freq changes.
+ * @devfreq: the devfreq instance.
+ */
+static int devfreq_qos_notifier_call(struct devfreq *devfreq)
+{
+ int ret;
+
+ mutex_lock(&devfreq->lock);
+ ret = update_devfreq(devfreq);
+ mutex_unlock(&devfreq->lock);
+
+ return ret;
+}
+
+/**
+ * devfreq_qos_min_notifier_call() - Callback for qos min_freq changes.
+ * @nb: Should to be devfreq->nb_min
+ */
+static int devfreq_qos_min_notifier_call(struct notifier_block *nb,
+ unsigned long val, void *ptr)
+{
+ struct devfreq *devfreq = container_of(nb, struct devfreq, nb_min);
+
+ return devfreq_qos_notifier_call(devfreq);
+}
+
+/**
+ * devfreq_qos_max_notifier_call() - Callback for qos min_freq changes.
+ * @nb: Should to be devfreq->nb_max
+ */
+static int devfreq_qos_max_notifier_call(struct notifier_block *nb,
+ unsigned long val, void *ptr)
+{
+ struct devfreq *devfreq = container_of(nb, struct devfreq, nb_max);
+
+ return devfreq_qos_notifier_call(devfreq);
+}
+
/**
* devfreq_dev_release() - Callback for struct device to release the device.
* @dev: the devfreq device
*
* Remove devfreq from the list and release its resources.
@@ -623,10 +671,15 @@ static void devfreq_dev_release(struct device *dev)
return;
}
list_del(&devfreq->node);
mutex_unlock(&devfreq_list_lock);
+ dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_max,
+ DEV_PM_QOS_MAX_FREQUENCY);
+ dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_min,
+ DEV_PM_QOS_MIN_FREQUENCY);
+
if (devfreq->profile->exit)
devfreq->profile->exit(devfreq->dev.parent);
mutex_destroy(&devfreq->lock);
kfree(devfreq);
@@ -739,10 +792,27 @@ struct devfreq *devfreq_add_device(struct device *dev,
devfreq->last_stat_updated = jiffies;
srcu_init_notifier_head(&devfreq->transition_notifier_list);
+ /*
+ * Register notifiers for updates to min_freq/max_freq after device is
+ * initialized (and we can handle notifications) but before the governor
+ * is started (which should do an initial enforcement of constraints)
+ */
+ devfreq->nb_min.notifier_call = devfreq_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 = devfreq_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);
if (IS_ERR(governor)) {
dev_err(dev, "%s: Unable to find governor for the device\n",
@@ -134,10 +134,12 @@ struct devfreq_dev_profile {
* @total_trans: Number of devfreq transitions
* @trans_table: Statistics of devfreq transitions
* @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.
*
* Note that when a governor accesses entries in struct devfreq in its
* functions except for the context of callbacks defined in struct
@@ -176,10 +178,13 @@ struct devfreq {
unsigned int *trans_table;
unsigned long *time_in_state;
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 {
unsigned long old;
unsigned long new;
Register notifiers with the pm_qos framework in order to respond to requests for MIN_FREQUENCY and MAX_FREQUENCY. Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com> --- drivers/devfreq/devfreq.c | 70 +++++++++++++++++++++++++++++++++++++++ include/linux/devfreq.h | 5 +++ 2 files changed, 75 insertions(+)