diff options
Diffstat (limited to 'drivers/devfreq/devfreq.c')
-rw-r--r-- | drivers/devfreq/devfreq.c | 129 |
1 files changed, 62 insertions, 67 deletions
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index a324801d6a66..551a271353d2 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -111,18 +111,16 @@ static void devfreq_set_freq_table(struct devfreq *devfreq) return; } - rcu_read_lock(); for (i = 0, freq = 0; i < profile->max_state; i++, freq++) { opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &freq); if (IS_ERR(opp)) { devm_kfree(devfreq->dev.parent, profile->freq_table); profile->max_state = 0; - rcu_read_unlock(); return; } + dev_pm_opp_put(opp); profile->freq_table[i] = freq; } - rcu_read_unlock(); } /** @@ -130,7 +128,7 @@ static void devfreq_set_freq_table(struct devfreq *devfreq) * @devfreq: the devfreq instance * @freq: the update target frequency */ -static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq) +int devfreq_update_status(struct devfreq *devfreq, unsigned long freq) { int lev, prev_lev, ret = 0; unsigned long cur_time; @@ -166,6 +164,7 @@ out: devfreq->last_stat_updated = cur_time; return ret; } +EXPORT_SYMBOL(devfreq_update_status); /** * find_devfreq_governor() - find devfreq governor from name @@ -474,11 +473,15 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type, } /** - * _remove_devfreq() - Remove devfreq from the list and release its resources. - * @devfreq: the devfreq struct + * devfreq_dev_release() - Callback for struct device to release the device. + * @dev: the devfreq device + * + * Remove devfreq from the list and release its resources. */ -static void _remove_devfreq(struct devfreq *devfreq) +static void devfreq_dev_release(struct device *dev) { + struct devfreq *devfreq = to_devfreq(dev); + mutex_lock(&devfreq_list_lock); if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) { mutex_unlock(&devfreq_list_lock); @@ -500,19 +503,6 @@ static void _remove_devfreq(struct devfreq *devfreq) } /** - * devfreq_dev_release() - Callback for struct device to release the device. - * @dev: the devfreq device - * - * This calls _remove_devfreq() if _remove_devfreq() is not called. - */ -static void devfreq_dev_release(struct device *dev) -{ - struct devfreq *devfreq = to_devfreq(dev); - - _remove_devfreq(devfreq); -} - -/** * devfreq_add_device() - Add devfreq feature to the device * @dev: the device to add devfreq feature. * @profile: device-specific profile to run devfreq. @@ -527,6 +517,7 @@ struct devfreq *devfreq_add_device(struct device *dev, { struct devfreq *devfreq; struct devfreq_governor *governor; + static atomic_t devfreq_no = ATOMIC_INIT(-1); int err = 0; if (!dev || !profile || !governor_name) { @@ -538,15 +529,14 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq = find_device_devfreq(dev); mutex_unlock(&devfreq_list_lock); if (!IS_ERR(devfreq)) { - dev_err(dev, "%s: Unable to create devfreq for the device. It already has one.\n", __func__); + dev_err(dev, "%s: Unable to create devfreq for the device.\n", + __func__); err = -EINVAL; goto err_out; } devfreq = kzalloc(sizeof(struct devfreq), GFP_KERNEL); if (!devfreq) { - dev_err(dev, "%s: Unable to create devfreq for the device\n", - __func__); err = -ENOMEM; goto err_out; } @@ -569,18 +559,21 @@ struct devfreq *devfreq_add_device(struct device *dev, mutex_lock(&devfreq->lock); } - dev_set_name(&devfreq->dev, "%s", dev_name(dev)); + dev_set_name(&devfreq->dev, "devfreq%d", + atomic_inc_return(&devfreq_no)); err = device_register(&devfreq->dev); if (err) { mutex_unlock(&devfreq->lock); goto err_out; } - devfreq->trans_table = devm_kzalloc(&devfreq->dev, sizeof(unsigned int) * + devfreq->trans_table = devm_kzalloc(&devfreq->dev, + sizeof(unsigned int) * devfreq->profile->max_state * devfreq->profile->max_state, GFP_KERNEL); - devfreq->time_in_state = devm_kzalloc(&devfreq->dev, sizeof(unsigned long) * + devfreq->time_in_state = devm_kzalloc(&devfreq->dev, + sizeof(unsigned long) * devfreq->profile->max_state, GFP_KERNEL); devfreq->last_stat_updated = jiffies; @@ -593,11 +586,16 @@ struct devfreq *devfreq_add_device(struct device *dev, list_add(&devfreq->node, &devfreq_list); governor = find_devfreq_governor(devfreq->governor_name); - if (!IS_ERR(governor)) - devfreq->governor = governor; - if (devfreq->governor) - err = devfreq->governor->event_handler(devfreq, - DEVFREQ_GOV_START, NULL); + if (IS_ERR(governor)) { + dev_err(dev, "%s: Unable to find governor for the device\n", + __func__); + err = PTR_ERR(governor); + goto err_init; + } + + devfreq->governor = governor; + 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__); @@ -934,6 +932,9 @@ 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) { + ret = -EINVAL; + goto out; } if (df->governor) { @@ -963,13 +964,33 @@ static ssize_t available_governors_show(struct device *d, struct device_attribute *attr, char *buf) { - struct devfreq_governor *tmp_governor; + struct devfreq *df = to_devfreq(d); ssize_t count = 0; mutex_lock(&devfreq_list_lock); - list_for_each_entry(tmp_governor, &devfreq_governor_list, node) - count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), - "%s ", tmp_governor->name); + + /* + * The devfreq with immutable governor (e.g., passive) shows + * only own governor. + */ + if (df->governor->immutable) { + count = scnprintf(&buf[count], DEVFREQ_NAME_LEN, + "%s ", df->governor_name); + /* + * The devfreq device shows the registered governor except for + * immutable governors such as passive governor . + */ + } else { + struct devfreq_governor *governor; + + list_for_each_entry(governor, &devfreq_governor_list, node) { + if (governor->immutable) + continue; + count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), + "%s ", governor->name); + } + } + mutex_unlock(&devfreq_list_lock); /* Truncate the trailing space */ @@ -990,7 +1011,7 @@ static ssize_t cur_freq_show(struct device *dev, struct device_attribute *attr, if (devfreq->profile->get_cur_freq && !devfreq->profile->get_cur_freq(devfreq->dev.parent, &freq)) - return sprintf(buf, "%lu\n", freq); + return sprintf(buf, "%lu\n", freq); return sprintf(buf, "%lu\n", devfreq->previous_freq); } @@ -1107,17 +1128,16 @@ static ssize_t available_frequencies_show(struct device *d, ssize_t count = 0; unsigned long freq = 0; - rcu_read_lock(); do { opp = dev_pm_opp_find_freq_ceil(dev, &freq); if (IS_ERR(opp)) break; + dev_pm_opp_put(opp); count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), "%lu ", freq); freq++; } while (1); - rcu_read_unlock(); /* Truncate the trailing space */ if (count) @@ -1219,11 +1239,8 @@ subsys_initcall(devfreq_init); * @freq: The frequency given to target function * @flags: Flags handed from devfreq framework. * - * Locking: This function must be called under rcu_read_lock(). opp is a rcu - * protected pointer. The reason for the same is that the opp pointer which is - * returned will remain valid for use with opp_get_{voltage, freq} only while - * under the locked area. The pointer returned must be used prior to unlocking - * with rcu_read_unlock() to maintain the integrity of the pointer. + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. */ struct dev_pm_opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq, @@ -1260,18 +1277,7 @@ EXPORT_SYMBOL(devfreq_recommended_opp); */ int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq) { - struct srcu_notifier_head *nh; - int ret = 0; - - rcu_read_lock(); - nh = dev_pm_opp_get_notifier(dev); - if (IS_ERR(nh)) - ret = PTR_ERR(nh); - rcu_read_unlock(); - if (!ret) - ret = srcu_notifier_chain_register(nh, &devfreq->nb); - - return ret; + return dev_pm_opp_register_notifier(dev, &devfreq->nb); } EXPORT_SYMBOL(devfreq_register_opp_notifier); @@ -1287,18 +1293,7 @@ EXPORT_SYMBOL(devfreq_register_opp_notifier); */ int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq) { - struct srcu_notifier_head *nh; - int ret = 0; - - rcu_read_lock(); - nh = dev_pm_opp_get_notifier(dev); - if (IS_ERR(nh)) - ret = PTR_ERR(nh); - rcu_read_unlock(); - if (!ret) - ret = srcu_notifier_chain_unregister(nh, &devfreq->nb); - - return ret; + return dev_pm_opp_unregister_notifier(dev, &devfreq->nb); } EXPORT_SYMBOL(devfreq_unregister_opp_notifier); |