summaryrefslogtreecommitdiff
path: root/drivers/devfreq/devfreq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/devfreq/devfreq.c')
-rw-r--r--drivers/devfreq/devfreq.c242
1 files changed, 163 insertions, 79 deletions
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index 861c100f9fac..6aa10de792b3 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -31,6 +31,8 @@
#define CREATE_TRACE_POINTS
#include <trace/events/devfreq.h>
+#define IS_SUPPORTED_FLAG(f, name) ((f & DEVFREQ_GOV_FLAG_##name) ? true : false)
+#define IS_SUPPORTED_ATTR(f, name) ((f & DEVFREQ_GOV_ATTR_##name) ? true : false)
#define HZ_PER_KHZ 1000
static struct class *devfreq_class;
@@ -367,6 +369,14 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq,
return err;
}
+ /*
+ * Print devfreq_frequency trace information between DEVFREQ_PRECHANGE
+ * and DEVFREQ_POSTCHANGE because for showing the correct frequency
+ * change order of between devfreq device and passive devfreq device.
+ */
+ if (trace_devfreq_frequency_enabled() && new_freq != cur_freq)
+ trace_devfreq_frequency(devfreq, new_freq, cur_freq);
+
freqs.new = new_freq;
devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE);
@@ -382,18 +392,19 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq,
return err;
}
-/* Load monitoring helper functions for governors use */
-
/**
- * update_devfreq() - Reevaluate the device and configure frequency.
+ * devfreq_update_target() - Reevaluate the device and configure frequency
+ * on the final stage.
* @devfreq: the devfreq instance.
+ * @freq: the new frequency of parent device. This argument
+ * is only used for devfreq device using passive governor.
*
- * Note: Lock devfreq->lock before calling update_devfreq
- * This function is exported for governors.
+ * Note: Lock devfreq->lock before calling devfreq_update_target. This function
+ * should be only used by both update_devfreq() and devfreq governors.
*/
-int update_devfreq(struct devfreq *devfreq)
+int devfreq_update_target(struct devfreq *devfreq, unsigned long freq)
{
- unsigned long freq, min_freq, max_freq;
+ unsigned long min_freq, max_freq;
int err = 0;
u32 flags = 0;
@@ -418,7 +429,21 @@ int update_devfreq(struct devfreq *devfreq)
}
return devfreq_set_target(devfreq, freq, flags);
+}
+EXPORT_SYMBOL(devfreq_update_target);
+/* Load monitoring helper functions for governors use */
+
+/**
+ * update_devfreq() - Reevaluate the device and configure frequency.
+ * @devfreq: the devfreq instance.
+ *
+ * Note: Lock devfreq->lock before calling update_devfreq
+ * This function is exported for governors.
+ */
+int update_devfreq(struct devfreq *devfreq)
+{
+ return devfreq_update_target(devfreq, 0L);
}
EXPORT_SYMBOL(update_devfreq);
@@ -456,7 +481,7 @@ static void devfreq_monitor(struct work_struct *work)
*/
void devfreq_monitor_start(struct devfreq *devfreq)
{
- if (devfreq->governor->interrupt_driven)
+ if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
return;
switch (devfreq->profile->timer) {
@@ -486,7 +511,7 @@ EXPORT_SYMBOL(devfreq_monitor_start);
*/
void devfreq_monitor_stop(struct devfreq *devfreq)
{
- if (devfreq->governor->interrupt_driven)
+ if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
return;
cancel_delayed_work_sync(&devfreq->work);
@@ -517,7 +542,7 @@ void devfreq_monitor_suspend(struct devfreq *devfreq)
devfreq->stop_polling = true;
mutex_unlock(&devfreq->lock);
- if (devfreq->governor->interrupt_driven)
+ if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
return;
cancel_delayed_work_sync(&devfreq->work);
@@ -537,12 +562,13 @@ void devfreq_monitor_resume(struct devfreq *devfreq)
unsigned long freq;
mutex_lock(&devfreq->lock);
- if (!devfreq->stop_polling)
- goto out;
- if (devfreq->governor->interrupt_driven)
+ if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
goto out_update;
+ if (!devfreq->stop_polling)
+ goto out;
+
if (!delayed_work_pending(&devfreq->work) &&
devfreq->profile->polling_ms)
queue_delayed_work(devfreq_wq, &devfreq->work,
@@ -577,10 +603,10 @@ void devfreq_update_interval(struct devfreq *devfreq, unsigned int *delay)
mutex_lock(&devfreq->lock);
devfreq->profile->polling_ms = new_delay;
- if (devfreq->stop_polling)
+ if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
goto out;
- if (devfreq->governor->interrupt_driven)
+ if (devfreq->stop_polling)
goto out;
/* if new delay is zero, stop polling */
@@ -735,6 +761,11 @@ static void devfreq_dev_release(struct device *dev)
kfree(devfreq);
}
+static void create_sysfs_files(struct devfreq *devfreq,
+ const struct devfreq_governor *gov);
+static void remove_sysfs_files(struct devfreq *devfreq,
+ const struct devfreq_governor *gov);
+
/**
* devfreq_add_device() - Add devfreq feature to the device
* @dev: the device to add devfreq feature.
@@ -780,7 +811,6 @@ struct devfreq *devfreq_add_device(struct device *dev,
devfreq->dev.release = devfreq_dev_release;
INIT_LIST_HEAD(&devfreq->node);
devfreq->profile = profile;
- strscpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN);
devfreq->previous_freq = profile->initial_freq;
devfreq->last_status.current_frequency = profile->initial_freq;
devfreq->data = data;
@@ -876,7 +906,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
mutex_lock(&devfreq_list_lock);
- governor = try_then_request_governor(devfreq->governor_name);
+ governor = try_then_request_governor(governor_name);
if (IS_ERR(governor)) {
dev_err(dev, "%s: Unable to find governor for the device\n",
__func__);
@@ -892,6 +922,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
__func__);
goto err_init;
}
+ create_sysfs_files(devfreq, devfreq->governor);
list_add(&devfreq->node, &devfreq_list);
@@ -922,9 +953,12 @@ int devfreq_remove_device(struct devfreq *devfreq)
if (!devfreq)
return -EINVAL;
- if (devfreq->governor)
+ if (devfreq->governor) {
devfreq->governor->event_handler(devfreq,
DEVFREQ_GOV_STOP, NULL);
+ remove_sysfs_files(devfreq, devfreq->governor);
+ }
+
device_unregister(&devfreq->dev);
return 0;
@@ -1214,7 +1248,7 @@ int devfreq_add_governor(struct devfreq_governor *governor)
int ret = 0;
struct device *dev = devfreq->dev.parent;
- if (!strncmp(devfreq->governor_name, governor->name,
+ if (!strncmp(devfreq->governor->name, governor->name,
DEVFREQ_NAME_LEN)) {
/* The following should never occur */
if (devfreq->governor) {
@@ -1276,7 +1310,7 @@ int devfreq_remove_governor(struct devfreq_governor *governor)
int ret;
struct device *dev = devfreq->dev.parent;
- if (!strncmp(devfreq->governor_name, governor->name,
+ if (!strncmp(devfreq->governor->name, governor->name,
DEVFREQ_NAME_LEN)) {
/* we should have a devfreq governor! */
if (!devfreq->governor) {
@@ -1347,36 +1381,53 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
if (df->governor == governor) {
ret = 0;
goto out;
- } else if (df->governor->immutable || governor->immutable) {
+ } else if (IS_SUPPORTED_FLAG(df->governor->flags, IMMUTABLE)
+ || IS_SUPPORTED_FLAG(governor->flags, IMMUTABLE)) {
ret = -EINVAL;
goto out;
}
+ /*
+ * Stop the current governor and remove the specific sysfs files
+ * which depend on current governor.
+ */
ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL);
if (ret) {
dev_warn(dev, "%s: Governor %s not stopped(%d)\n",
__func__, df->governor->name, ret);
goto out;
}
+ remove_sysfs_files(df, df->governor);
+ /*
+ * Start the new governor and create the specific sysfs files
+ * which depend on the new governor.
+ */
prev_governor = df->governor;
df->governor = governor;
- strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN);
ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
if (ret) {
dev_warn(dev, "%s: Governor %s not started(%d)\n",
__func__, df->governor->name, ret);
+
+ /* Restore previous governor */
df->governor = prev_governor;
- strncpy(df->governor_name, prev_governor->name,
- DEVFREQ_NAME_LEN);
ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
if (ret) {
dev_err(dev,
"%s: reverting to Governor %s failed (%d)\n",
- __func__, df->governor_name, ret);
+ __func__, prev_governor->name, ret);
df->governor = NULL;
+ goto out;
}
}
+
+ /*
+ * Create the sysfs files for the new governor. But if failed to start
+ * the new governor, restore the sysfs files of previous governor.
+ */
+ create_sysfs_files(df, df->governor);
+
out:
mutex_unlock(&devfreq_list_lock);
@@ -1402,9 +1453,9 @@ static ssize_t available_governors_show(struct device *d,
* The devfreq with immutable governor (e.g., passive) shows
* only own governor.
*/
- if (df->governor->immutable) {
+ if (IS_SUPPORTED_FLAG(df->governor->flags, IMMUTABLE)) {
count = scnprintf(&buf[count], DEVFREQ_NAME_LEN,
- "%s ", df->governor_name);
+ "%s ", df->governor->name);
/*
* The devfreq device shows the registered governor except for
* immutable governors such as passive governor .
@@ -1413,7 +1464,7 @@ static ssize_t available_governors_show(struct device *d,
struct devfreq_governor *governor;
list_for_each_entry(governor, &devfreq_governor_list, node) {
- if (governor->immutable)
+ if (IS_SUPPORTED_FLAG(governor->flags, IMMUTABLE))
continue;
count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
"%s ", governor->name);
@@ -1458,39 +1509,6 @@ static ssize_t target_freq_show(struct device *dev,
}
static DEVICE_ATTR_RO(target_freq);
-static ssize_t polling_interval_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct devfreq *df = to_devfreq(dev);
-
- if (!df->profile)
- return -EINVAL;
-
- return sprintf(buf, "%d\n", df->profile->polling_ms);
-}
-
-static ssize_t polling_interval_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct devfreq *df = to_devfreq(dev);
- unsigned int value;
- int ret;
-
- if (!df->governor)
- return -EINVAL;
-
- ret = sscanf(buf, "%u", &value);
- if (ret != 1)
- return -EINVAL;
-
- df->governor->event_handler(df, DEVFREQ_GOV_UPDATE_INTERVAL, &value);
- ret = count;
-
- return ret;
-}
-static DEVICE_ATTR_RW(polling_interval);
-
static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -1698,6 +1716,53 @@ static ssize_t trans_stat_store(struct device *dev,
}
static DEVICE_ATTR_RW(trans_stat);
+static struct attribute *devfreq_attrs[] = {
+ &dev_attr_name.attr,
+ &dev_attr_governor.attr,
+ &dev_attr_available_governors.attr,
+ &dev_attr_cur_freq.attr,
+ &dev_attr_available_frequencies.attr,
+ &dev_attr_target_freq.attr,
+ &dev_attr_min_freq.attr,
+ &dev_attr_max_freq.attr,
+ &dev_attr_trans_stat.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(devfreq);
+
+static ssize_t polling_interval_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct devfreq *df = to_devfreq(dev);
+
+ if (!df->profile)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", df->profile->polling_ms);
+}
+
+static ssize_t polling_interval_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct devfreq *df = to_devfreq(dev);
+ unsigned int value;
+ int ret;
+
+ if (!df->governor)
+ return -EINVAL;
+
+ ret = sscanf(buf, "%u", &value);
+ if (ret != 1)
+ return -EINVAL;
+
+ df->governor->event_handler(df, DEVFREQ_GOV_UPDATE_INTERVAL, &value);
+ ret = count;
+
+ return ret;
+}
+static DEVICE_ATTR_RW(polling_interval);
+
static ssize_t timer_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1761,21 +1826,36 @@ out:
}
static DEVICE_ATTR_RW(timer);
-static struct attribute *devfreq_attrs[] = {
- &dev_attr_name.attr,
- &dev_attr_governor.attr,
- &dev_attr_available_governors.attr,
- &dev_attr_cur_freq.attr,
- &dev_attr_available_frequencies.attr,
- &dev_attr_target_freq.attr,
- &dev_attr_polling_interval.attr,
- &dev_attr_min_freq.attr,
- &dev_attr_max_freq.attr,
- &dev_attr_trans_stat.attr,
- &dev_attr_timer.attr,
- NULL,
-};
-ATTRIBUTE_GROUPS(devfreq);
+#define CREATE_SYSFS_FILE(df, name) \
+{ \
+ int ret; \
+ ret = sysfs_create_file(&df->dev.kobj, &dev_attr_##name.attr); \
+ if (ret < 0) { \
+ dev_warn(&df->dev, \
+ "Unable to create attr(%s)\n", "##name"); \
+ } \
+} \
+
+/* Create the specific sysfs files which depend on each governor. */
+static void create_sysfs_files(struct devfreq *devfreq,
+ const struct devfreq_governor *gov)
+{
+ if (IS_SUPPORTED_ATTR(gov->attrs, POLLING_INTERVAL))
+ CREATE_SYSFS_FILE(devfreq, polling_interval);
+ if (IS_SUPPORTED_ATTR(gov->attrs, TIMER))
+ CREATE_SYSFS_FILE(devfreq, timer);
+}
+
+/* Remove the specific sysfs files which depend on each governor. */
+static void remove_sysfs_files(struct devfreq *devfreq,
+ const struct devfreq_governor *gov)
+{
+ if (IS_SUPPORTED_ATTR(gov->attrs, POLLING_INTERVAL))
+ sysfs_remove_file(&devfreq->dev.kobj,
+ &dev_attr_polling_interval.attr);
+ if (IS_SUPPORTED_ATTR(gov->attrs, TIMER))
+ sysfs_remove_file(&devfreq->dev.kobj, &dev_attr_timer.attr);
+}
/**
* devfreq_summary_show() - Show the summary of the devfreq devices
@@ -1818,7 +1898,7 @@ static int devfreq_summary_show(struct seq_file *s, void *data)
list_for_each_entry_reverse(devfreq, &devfreq_list, node) {
#if IS_ENABLED(CONFIG_DEVFREQ_GOV_PASSIVE)
- if (!strncmp(devfreq->governor_name, DEVFREQ_GOV_PASSIVE,
+ if (!strncmp(devfreq->governor->name, DEVFREQ_GOV_PASSIVE,
DEVFREQ_NAME_LEN)) {
struct devfreq_passive_data *data = devfreq->data;
@@ -1832,15 +1912,19 @@ static int devfreq_summary_show(struct seq_file *s, void *data)
mutex_lock(&devfreq->lock);
cur_freq = devfreq->previous_freq;
get_freq_range(devfreq, &min_freq, &max_freq);
- polling_ms = devfreq->profile->polling_ms;
timer = devfreq->profile->timer;
+
+ if (IS_SUPPORTED_ATTR(devfreq->governor->attrs, POLLING_INTERVAL))
+ polling_ms = devfreq->profile->polling_ms;
+ else
+ polling_ms = 0;
mutex_unlock(&devfreq->lock);
seq_printf(s,
"%-30s %-30s %-15s %-10s %10d %12ld %12ld %12ld\n",
dev_name(&devfreq->dev),
p_devfreq ? dev_name(&p_devfreq->dev) : "null",
- devfreq->governor_name,
+ devfreq->governor->name,
polling_ms ? timer_name[timer] : "null",
polling_ms,
cur_freq,