diff options
Diffstat (limited to 'drivers/devfreq/devfreq.c')
| -rw-r--r-- | drivers/devfreq/devfreq.c | 257 |
1 files changed, 159 insertions, 98 deletions
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 28f3e0ba6cdd..00979f2e0e27 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -20,6 +20,7 @@ #include <linux/stat.h> #include <linux/pm_opp.h> #include <linux/devfreq.h> +#include <linux/devfreq-governor.h> #include <linux/workqueue.h> #include <linux/platform_device.h> #include <linux/list.h> @@ -27,14 +28,13 @@ #include <linux/hrtimer.h> #include <linux/of.h> #include <linux/pm_qos.h> -#include "governor.h" +#include <linux/units.h> #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; static struct dentry *devfreq_debugfs; @@ -88,7 +88,7 @@ static unsigned long find_available_min_freq(struct devfreq *devfreq) struct dev_pm_opp *opp; unsigned long min_freq = 0; - opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &min_freq); + opp = dev_pm_opp_find_freq_ceil_indexed(devfreq->dev.parent, &min_freq, 0); if (IS_ERR(opp)) min_freq = 0; else @@ -102,7 +102,7 @@ static unsigned long find_available_max_freq(struct devfreq *devfreq) struct dev_pm_opp *opp; unsigned long max_freq = ULONG_MAX; - opp = dev_pm_opp_find_freq_floor(devfreq->dev.parent, &max_freq); + opp = dev_pm_opp_find_freq_floor_indexed(devfreq->dev.parent, &max_freq, 0); if (IS_ERR(opp)) max_freq = 0; else @@ -112,18 +112,18 @@ static unsigned long find_available_max_freq(struct devfreq *devfreq) } /** - * get_freq_range() - Get the current freq range + * devfreq_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) +void devfreq_get_freq_range(struct devfreq *devfreq, + unsigned long *min_freq, + unsigned long *max_freq) { - unsigned long *freq_table = devfreq->profile->freq_table; + unsigned long *freq_table = devfreq->freq_table; s32 qos_min_freq, qos_max_freq; lockdep_assert_held(&devfreq->lock); @@ -133,11 +133,11 @@ static void get_freq_range(struct devfreq *devfreq, * 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]) { + if (freq_table[0] < freq_table[devfreq->max_state - 1]) { *min_freq = freq_table[0]; - *max_freq = freq_table[devfreq->profile->max_state - 1]; + *max_freq = freq_table[devfreq->max_state - 1]; } else { - *min_freq = freq_table[devfreq->profile->max_state - 1]; + *min_freq = freq_table[devfreq->max_state - 1]; *max_freq = freq_table[0]; } @@ -152,12 +152,10 @@ static void get_freq_range(struct devfreq *devfreq, (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; + *max_freq = clamp(*max_freq, devfreq->scaling_min_freq, devfreq->scaling_max_freq); + *min_freq = clamp(*min_freq, devfreq->scaling_min_freq, *max_freq); } +EXPORT_SYMBOL(devfreq_get_freq_range); /** * devfreq_get_freq_level() - Lookup freq_table for the frequency @@ -168,8 +166,8 @@ static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq) { int lev; - for (lev = 0; lev < devfreq->profile->max_state; lev++) - if (freq == devfreq->profile->freq_table[lev]) + for (lev = 0; lev < devfreq->max_state; lev++) + if (freq == devfreq->freq_table[lev]) return lev; return -EINVAL; @@ -177,7 +175,6 @@ static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq) static int set_freq_table(struct devfreq *devfreq) { - struct devfreq_dev_profile *profile = devfreq->profile; struct dev_pm_opp *opp; unsigned long freq; int i, count; @@ -187,25 +184,22 @@ static int set_freq_table(struct devfreq *devfreq) if (count <= 0) return -EINVAL; - profile->max_state = count; - profile->freq_table = devm_kcalloc(devfreq->dev.parent, - profile->max_state, - sizeof(*profile->freq_table), - GFP_KERNEL); - if (!profile->freq_table) { - profile->max_state = 0; + devfreq->max_state = count; + devfreq->freq_table = devm_kcalloc(devfreq->dev.parent, + devfreq->max_state, + sizeof(*devfreq->freq_table), + GFP_KERNEL); + if (!devfreq->freq_table) return -ENOMEM; - } - for (i = 0, freq = 0; i < profile->max_state; i++, freq++) { - opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &freq); + for (i = 0, freq = 0; i < devfreq->max_state; i++, freq++) { + opp = dev_pm_opp_find_freq_ceil_indexed(devfreq->dev.parent, &freq, 0); if (IS_ERR(opp)) { - devm_kfree(devfreq->dev.parent, profile->freq_table); - profile->max_state = 0; + devm_kfree(devfreq->dev.parent, devfreq->freq_table); return PTR_ERR(opp); } dev_pm_opp_put(opp); - profile->freq_table[i] = freq; + devfreq->freq_table[i] = freq; } return 0; @@ -245,7 +239,7 @@ int devfreq_update_status(struct devfreq *devfreq, unsigned long freq) if (lev != prev_lev) { devfreq->stats.trans_table[ - (prev_lev * devfreq->profile->max_state) + lev]++; + (prev_lev * devfreq->max_state) + lev]++; devfreq->stats.total_trans++; } @@ -382,8 +376,8 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq, devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE); if (devfreq_update_status(devfreq, new_freq)) - dev_err(&devfreq->dev, - "Couldn't update frequency transition information.\n"); + dev_warn(&devfreq->dev, + "Couldn't update frequency transition information.\n"); devfreq->previous_freq = new_freq; @@ -418,7 +412,7 @@ int devfreq_update_target(struct devfreq *devfreq, unsigned long freq) err = devfreq->governor->get_target_freq(devfreq, &freq); if (err) return err; - get_freq_range(devfreq, &min_freq, &max_freq); + devfreq_get_freq_range(devfreq, &min_freq, &max_freq); if (freq < min_freq) { freq = min_freq; @@ -464,10 +458,14 @@ static void devfreq_monitor(struct work_struct *work) if (err) dev_err(&devfreq->dev, "dvfs failed with (%d) error\n", err); + if (devfreq->stop_polling) + goto out; + queue_delayed_work(devfreq_wq, &devfreq->work, msecs_to_jiffies(devfreq->profile->polling_ms)); - mutex_unlock(&devfreq->lock); +out: + mutex_unlock(&devfreq->lock); trace_devfreq_monitor(devfreq); } @@ -475,16 +473,21 @@ static void devfreq_monitor(struct work_struct *work) * devfreq_monitor_start() - Start load monitoring of devfreq instance * @devfreq: the devfreq instance. * - * Helper function for starting devfreq device load monitoring. By - * default delayed work based monitoring is supported. Function - * to be called from governor in response to DEVFREQ_GOV_START - * event when device is added to devfreq framework. + * Helper function for starting devfreq device load monitoring. By default, + * deferrable timer is used for load monitoring. But the users can change this + * behavior using the "timer" type in devfreq_dev_profile. This function will be + * called by devfreq governor in response to the DEVFREQ_GOV_START event + * generated while adding a device to the devfreq framework. */ void devfreq_monitor_start(struct devfreq *devfreq) { if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN)) return; + mutex_lock(&devfreq->lock); + if (delayed_work_pending(&devfreq->work)) + goto out; + switch (devfreq->profile->timer) { case DEVFREQ_TIMER_DEFERRABLE: INIT_DEFERRABLE_WORK(&devfreq->work, devfreq_monitor); @@ -493,12 +496,16 @@ void devfreq_monitor_start(struct devfreq *devfreq) INIT_DELAYED_WORK(&devfreq->work, devfreq_monitor); break; default: - return; + goto out; } if (devfreq->profile->polling_ms) queue_delayed_work(devfreq_wq, &devfreq->work, msecs_to_jiffies(devfreq->profile->polling_ms)); + +out: + devfreq->stop_polling = false; + mutex_unlock(&devfreq->lock); } EXPORT_SYMBOL(devfreq_monitor_start); @@ -515,6 +522,14 @@ void devfreq_monitor_stop(struct devfreq *devfreq) if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN)) return; + mutex_lock(&devfreq->lock); + if (devfreq->stop_polling) { + mutex_unlock(&devfreq->lock); + return; + } + + devfreq->stop_polling = true; + mutex_unlock(&devfreq->lock); cancel_delayed_work_sync(&devfreq->work); } EXPORT_SYMBOL(devfreq_monitor_stop); @@ -699,6 +714,8 @@ static int qos_notifier_call(struct devfreq *devfreq) /** * qos_min_notifier_call() - Callback for QoS min_freq changes. * @nb: Should be devfreq->nb_min + * @val: not used + * @ptr: not used */ static int qos_min_notifier_call(struct notifier_block *nb, unsigned long val, void *ptr) @@ -709,6 +726,8 @@ static int qos_min_notifier_call(struct notifier_block *nb, /** * qos_max_notifier_call() - Callback for QoS max_freq changes. * @nb: Should be devfreq->nb_max + * @val: not used + * @ptr: not used */ static int qos_max_notifier_call(struct notifier_block *nb, unsigned long val, void *ptr) @@ -762,6 +781,7 @@ static void devfreq_dev_release(struct device *dev) dev_pm_opp_put_opp_table(devfreq->opp_table); mutex_destroy(&devfreq->lock); + srcu_cleanup_notifier_head(&devfreq->transition_notifier_list); kfree(devfreq); } @@ -775,8 +795,7 @@ static void remove_sysfs_files(struct devfreq *devfreq, * @dev: the device to add devfreq feature. * @profile: device-specific profile to run devfreq. * @governor_name: name of the policy to choose frequency. - * @data: private data for the governor. The devfreq framework does not - * touch this value. + * @data: devfreq driver pass to governors, governor should not change it. */ struct devfreq *devfreq_add_device(struct device *dev, struct devfreq_dev_profile *profile, @@ -812,6 +831,7 @@ struct devfreq *devfreq_add_device(struct device *dev, mutex_lock(&devfreq->lock); devfreq->dev.parent = dev; devfreq->dev.class = devfreq_class; + devfreq->dev.groups = profile->dev_groups; devfreq->dev.release = devfreq_dev_release; INIT_LIST_HEAD(&devfreq->node); devfreq->profile = profile; @@ -827,12 +847,15 @@ struct devfreq *devfreq_add_device(struct device *dev, goto err_dev; } - if (!devfreq->profile->max_state && !devfreq->profile->freq_table) { + if (!devfreq->profile->max_state || !devfreq->profile->freq_table) { mutex_unlock(&devfreq->lock); err = set_freq_table(devfreq); if (err < 0) goto err_dev; mutex_lock(&devfreq->lock); + } else { + devfreq->freq_table = devfreq->profile->freq_table; + devfreq->max_state = devfreq->profile->max_state; } devfreq->scaling_min_freq = find_available_min_freq(devfreq); @@ -866,8 +889,8 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq->stats.trans_table = devm_kzalloc(&devfreq->dev, array3_size(sizeof(unsigned int), - devfreq->profile->max_state, - devfreq->profile->max_state), + devfreq->max_state, + devfreq->max_state), GFP_KERNEL); if (!devfreq->stats.trans_table) { mutex_unlock(&devfreq->lock); @@ -876,7 +899,7 @@ struct devfreq *devfreq_add_device(struct device *dev, } devfreq->stats.time_in_state = devm_kcalloc(&devfreq->dev, - devfreq->profile->max_state, + devfreq->max_state, sizeof(*devfreq->stats.time_in_state), GFP_KERNEL); if (!devfreq->stats.time_in_state) { @@ -928,8 +951,9 @@ struct devfreq *devfreq_add_device(struct device *dev, err = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_START, NULL); if (err) { - dev_err(dev, "%s: Unable to start governor for the device\n", - __func__); + dev_err_probe(dev, err, + "%s: Unable to start governor for the device\n", + __func__); goto err_init; } create_sysfs_files(devfreq, devfreq->governor); @@ -1003,8 +1027,7 @@ static void devm_devfreq_dev_release(struct device *dev, void *res) * @dev: the device to add devfreq feature. * @profile: device-specific profile to run devfreq. * @governor_name: name of the policy to choose frequency. - * @data: private data for the governor. The devfreq framework does not - * touch this value. + * @data: devfreq driver pass to governors, governor should not change it. * * This function manages automatically the memory of devfreq device using device * resource management and simplify the free operation for memory of devfreq @@ -1051,7 +1074,7 @@ struct devfreq *devfreq_get_devfreq_by_node(struct device_node *node) mutex_lock(&devfreq_list_lock); list_for_each_entry(devfreq, &devfreq_list, node) { if (devfreq->dev.parent - && devfreq->dev.parent->of_node == node) { + && device_match_of_node(devfreq->dev.parent, node)) { mutex_unlock(&devfreq_list_lock); return devfreq; } @@ -1301,6 +1324,32 @@ err_out: } EXPORT_SYMBOL(devfreq_add_governor); +static void devm_devfreq_remove_governor(void *governor) +{ + WARN_ON(devfreq_remove_governor(governor)); +} + +/** + * devm_devfreq_add_governor() - Add devfreq governor + * @dev: device which adds devfreq governor + * @governor: the devfreq governor to be added + * + * This is a resource-managed variant of devfreq_add_governor(). + */ +int devm_devfreq_add_governor(struct device *dev, + struct devfreq_governor *governor) +{ + int err; + + err = devfreq_add_governor(governor); + if (err) + return err; + + return devm_add_action_or_reset(dev, devm_devfreq_remove_governor, + governor); +} +EXPORT_SYMBOL(devm_devfreq_add_governor); + /** * devfreq_remove_governor() - Remove devfreq feature from a device. * @governor: the devfreq governor to be removed @@ -1328,15 +1377,11 @@ int devfreq_remove_governor(struct devfreq_governor *governor) int ret; struct device *dev = devfreq->dev.parent; + if (!devfreq->governor) + continue; + if (!strncmp(devfreq->governor->name, governor->name, DEVFREQ_NAME_LEN)) { - /* we should have a devfreq governor! */ - if (!devfreq->governor) { - dev_warn(dev, "%s: Governor %s NOT present\n", - __func__, governor->name); - continue; - /* Fall through */ - } ret = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_STOP, NULL); if (ret) { @@ -1561,7 +1606,7 @@ static ssize_t min_freq_show(struct device *dev, struct device_attribute *attr, unsigned long min_freq, max_freq; mutex_lock(&df->lock); - get_freq_range(df, &min_freq, &max_freq); + devfreq_get_freq_range(df, &min_freq, &max_freq); mutex_unlock(&df->lock); return sprintf(buf, "%lu\n", min_freq); @@ -1615,7 +1660,7 @@ static ssize_t max_freq_show(struct device *dev, struct device_attribute *attr, unsigned long min_freq, max_freq; mutex_lock(&df->lock); - get_freq_range(df, &min_freq, &max_freq); + devfreq_get_freq_range(df, &min_freq, &max_freq); mutex_unlock(&df->lock); return sprintf(buf, "%lu\n", max_freq); @@ -1635,9 +1680,9 @@ static ssize_t available_frequencies_show(struct device *d, mutex_lock(&df->lock); - for (i = 0; i < df->profile->max_state; i++) + for (i = 0; i < df->max_state; i++) count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), - "%lu ", df->profile->freq_table[i]); + "%lu ", df->freq_table[i]); mutex_unlock(&df->lock); /* Truncate the trailing space */ @@ -1654,16 +1699,16 @@ static ssize_t trans_stat_show(struct device *dev, struct device_attribute *attr, char *buf) { struct devfreq *df = to_devfreq(dev); - ssize_t len; + ssize_t len = 0; int i, j; unsigned int max_state; if (!df->profile) return -EINVAL; - max_state = df->profile->max_state; + max_state = df->max_state; if (max_state == 0) - return sprintf(buf, "Not Supported.\n"); + return sysfs_emit(buf, "Not Supported.\n"); mutex_lock(&df->lock); if (!df->stop_polling && @@ -1673,33 +1718,49 @@ static ssize_t trans_stat_show(struct device *dev, } mutex_unlock(&df->lock); - len = sprintf(buf, " From : To\n"); - len += sprintf(buf + len, " :"); - for (i = 0; i < max_state; i++) - len += sprintf(buf + len, "%10lu", - df->profile->freq_table[i]); + len += sysfs_emit_at(buf, len, " From : To\n"); + len += sysfs_emit_at(buf, len, " :"); + for (i = 0; i < max_state; i++) { + if (len >= PAGE_SIZE - 1) + break; + len += sysfs_emit_at(buf, len, "%10lu", + df->freq_table[i]); + } - len += sprintf(buf + len, " time(ms)\n"); + if (len >= PAGE_SIZE - 1) + return PAGE_SIZE - 1; + len += sysfs_emit_at(buf, len, " time(ms)\n"); for (i = 0; i < max_state; i++) { - if (df->profile->freq_table[i] - == df->previous_freq) { - len += sprintf(buf + len, "*"); - } else { - len += sprintf(buf + len, " "); - } - len += sprintf(buf + len, "%10lu:", - df->profile->freq_table[i]); - for (j = 0; j < max_state; j++) - len += sprintf(buf + len, "%10u", + if (len >= PAGE_SIZE - 1) + break; + if (df->freq_table[i] == df->previous_freq) + len += sysfs_emit_at(buf, len, "*"); + else + len += sysfs_emit_at(buf, len, " "); + if (len >= PAGE_SIZE - 1) + break; + len += sysfs_emit_at(buf, len, "%10lu:", df->freq_table[i]); + for (j = 0; j < max_state; j++) { + if (len >= PAGE_SIZE - 1) + break; + len += sysfs_emit_at(buf, len, "%10u", df->stats.trans_table[(i * max_state) + j]); + } + if (len >= PAGE_SIZE - 1) + break; + len += sysfs_emit_at(buf, len, "%10llu\n", (u64) + jiffies64_to_msecs(df->stats.time_in_state[i])); + } - len += sprintf(buf + len, "%10llu\n", (u64) - jiffies64_to_msecs(df->stats.time_in_state[i])); + if (len < PAGE_SIZE - 1) + len += sysfs_emit_at(buf, len, "Total transition : %u\n", + df->stats.total_trans); + if (len >= PAGE_SIZE - 1) { + pr_warn_once("devfreq transition table exceeds PAGE_SIZE. Disabling\n"); + return -EFBIG; } - len += sprintf(buf + len, "Total transition : %u\n", - df->stats.total_trans); return len; } @@ -1713,7 +1774,7 @@ static ssize_t trans_stat_store(struct device *dev, if (!df->profile) return -EINVAL; - if (df->profile->max_state == 0) + if (df->max_state == 0) return count; err = kstrtoint(buf, 10, &value); @@ -1721,11 +1782,11 @@ static ssize_t trans_stat_store(struct device *dev, return -EINVAL; mutex_lock(&df->lock); - memset(df->stats.time_in_state, 0, (df->profile->max_state * + memset(df->stats.time_in_state, 0, (df->max_state * sizeof(*df->stats.time_in_state))); memset(df->stats.trans_table, 0, array3_size(sizeof(unsigned int), - df->profile->max_state, - df->profile->max_state)); + df->max_state, + df->max_state)); df->stats.total_trans = 0; df->stats.last_update = get_jiffies_64(); mutex_unlock(&df->lock); @@ -1929,7 +1990,7 @@ 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); + devfreq_get_freq_range(devfreq, &min_freq, &max_freq); timer = devfreq->profile->timer; if (IS_SUPPORTED_ATTR(devfreq->governor->attrs, POLLING_INTERVAL)) @@ -1958,7 +2019,7 @@ DEFINE_SHOW_ATTRIBUTE(devfreq_summary); static int __init devfreq_init(void) { - devfreq_class = class_create(THIS_MODULE, "devfreq"); + devfreq_class = class_create("devfreq"); if (IS_ERR(devfreq_class)) { pr_err("%s: couldn't create class\n", __FILE__); return PTR_ERR(devfreq_class); @@ -2004,18 +2065,18 @@ struct dev_pm_opp *devfreq_recommended_opp(struct device *dev, if (flags & DEVFREQ_FLAG_LEAST_UPPER_BOUND) { /* The freq is an upper bound. opp should be lower */ - opp = dev_pm_opp_find_freq_floor(dev, freq); + opp = dev_pm_opp_find_freq_floor_indexed(dev, freq, 0); /* If not available, use the closest opp */ if (opp == ERR_PTR(-ERANGE)) - opp = dev_pm_opp_find_freq_ceil(dev, freq); + opp = dev_pm_opp_find_freq_ceil_indexed(dev, freq, 0); } else { /* The freq is an lower bound. opp should be higher */ - opp = dev_pm_opp_find_freq_ceil(dev, freq); + opp = dev_pm_opp_find_freq_ceil_indexed(dev, freq, 0); /* If not available, use the closest opp */ if (opp == ERR_PTR(-ERANGE)) - opp = dev_pm_opp_find_freq_floor(dev, freq); + opp = dev_pm_opp_find_freq_floor_indexed(dev, freq, 0); } return opp; |
