Merge branch 'pm-opp'
* pm-opp: PM / OPP: using kfree_rcu() to simplify the code PM / OPP: predictable fail results for opp_find* functions, v2 PM / OPP: Export symbols for module usage. PM / OPP: RCU reclaim
This commit is contained in:
commit
26ab93438f
|
@ -23,6 +23,7 @@
|
|||
#include <linux/rcupdate.h>
|
||||
#include <linux/opp.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
/*
|
||||
* Internal data structure organization with the OPP layer library is as
|
||||
|
@ -65,6 +66,7 @@ struct opp {
|
|||
unsigned long u_volt;
|
||||
|
||||
struct device_opp *dev_opp;
|
||||
struct rcu_head head;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -160,6 +162,7 @@ unsigned long opp_get_voltage(struct opp *opp)
|
|||
|
||||
return v;
|
||||
}
|
||||
EXPORT_SYMBOL(opp_get_voltage);
|
||||
|
||||
/**
|
||||
* opp_get_freq() - Gets the frequency corresponding to an available opp
|
||||
|
@ -189,6 +192,7 @@ unsigned long opp_get_freq(struct opp *opp)
|
|||
|
||||
return f;
|
||||
}
|
||||
EXPORT_SYMBOL(opp_get_freq);
|
||||
|
||||
/**
|
||||
* opp_get_opp_count() - Get number of opps available in the opp list
|
||||
|
@ -221,6 +225,7 @@ int opp_get_opp_count(struct device *dev)
|
|||
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL(opp_get_opp_count);
|
||||
|
||||
/**
|
||||
* opp_find_freq_exact() - search for an exact frequency
|
||||
|
@ -230,7 +235,10 @@ int opp_get_opp_count(struct device *dev)
|
|||
*
|
||||
* Searches for exact match in the opp list and returns pointer to the matching
|
||||
* opp if found, else returns ERR_PTR in case of error and should be handled
|
||||
* using IS_ERR.
|
||||
* using IS_ERR. Error return values can be:
|
||||
* EINVAL: for bad pointer
|
||||
* ERANGE: no match found for search
|
||||
* ENODEV: if device not found in list of registered devices
|
||||
*
|
||||
* Note: available is a modifier for the search. if available=true, then the
|
||||
* match is for exact matching frequency and is available in the stored OPP
|
||||
|
@ -249,7 +257,7 @@ struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq,
|
|||
bool available)
|
||||
{
|
||||
struct device_opp *dev_opp;
|
||||
struct opp *temp_opp, *opp = ERR_PTR(-ENODEV);
|
||||
struct opp *temp_opp, *opp = ERR_PTR(-ERANGE);
|
||||
|
||||
dev_opp = find_device_opp(dev);
|
||||
if (IS_ERR(dev_opp)) {
|
||||
|
@ -268,6 +276,7 @@ struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq,
|
|||
|
||||
return opp;
|
||||
}
|
||||
EXPORT_SYMBOL(opp_find_freq_exact);
|
||||
|
||||
/**
|
||||
* opp_find_freq_ceil() - Search for an rounded ceil freq
|
||||
|
@ -278,7 +287,11 @@ struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq,
|
|||
* for a device.
|
||||
*
|
||||
* Returns matching *opp and refreshes *freq accordingly, else returns
|
||||
* ERR_PTR in case of error and should be handled using IS_ERR.
|
||||
* ERR_PTR in case of error and should be handled using IS_ERR. Error return
|
||||
* values can be:
|
||||
* EINVAL: for bad pointer
|
||||
* ERANGE: no match found for search
|
||||
* ENODEV: if device not found in list of registered devices
|
||||
*
|
||||
* 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
|
||||
|
@ -289,7 +302,7 @@ struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq,
|
|||
struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq)
|
||||
{
|
||||
struct device_opp *dev_opp;
|
||||
struct opp *temp_opp, *opp = ERR_PTR(-ENODEV);
|
||||
struct opp *temp_opp, *opp = ERR_PTR(-ERANGE);
|
||||
|
||||
if (!dev || !freq) {
|
||||
dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq);
|
||||
|
@ -298,7 +311,7 @@ struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq)
|
|||
|
||||
dev_opp = find_device_opp(dev);
|
||||
if (IS_ERR(dev_opp))
|
||||
return opp;
|
||||
return ERR_CAST(dev_opp);
|
||||
|
||||
list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) {
|
||||
if (temp_opp->available && temp_opp->rate >= *freq) {
|
||||
|
@ -310,6 +323,7 @@ struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq)
|
|||
|
||||
return opp;
|
||||
}
|
||||
EXPORT_SYMBOL(opp_find_freq_ceil);
|
||||
|
||||
/**
|
||||
* opp_find_freq_floor() - Search for a rounded floor freq
|
||||
|
@ -320,7 +334,11 @@ struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq)
|
|||
* for a device.
|
||||
*
|
||||
* Returns matching *opp and refreshes *freq accordingly, else returns
|
||||
* ERR_PTR in case of error and should be handled using IS_ERR.
|
||||
* ERR_PTR in case of error and should be handled using IS_ERR. Error return
|
||||
* values can be:
|
||||
* EINVAL: for bad pointer
|
||||
* ERANGE: no match found for search
|
||||
* ENODEV: if device not found in list of registered devices
|
||||
*
|
||||
* 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
|
||||
|
@ -331,7 +349,7 @@ struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq)
|
|||
struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq)
|
||||
{
|
||||
struct device_opp *dev_opp;
|
||||
struct opp *temp_opp, *opp = ERR_PTR(-ENODEV);
|
||||
struct opp *temp_opp, *opp = ERR_PTR(-ERANGE);
|
||||
|
||||
if (!dev || !freq) {
|
||||
dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq);
|
||||
|
@ -340,7 +358,7 @@ struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq)
|
|||
|
||||
dev_opp = find_device_opp(dev);
|
||||
if (IS_ERR(dev_opp))
|
||||
return opp;
|
||||
return ERR_CAST(dev_opp);
|
||||
|
||||
list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) {
|
||||
if (temp_opp->available) {
|
||||
|
@ -356,6 +374,7 @@ struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq)
|
|||
|
||||
return opp;
|
||||
}
|
||||
EXPORT_SYMBOL(opp_find_freq_floor);
|
||||
|
||||
/**
|
||||
* opp_add() - Add an OPP table from a table definitions
|
||||
|
@ -512,7 +531,7 @@ static int opp_set_availability(struct device *dev, unsigned long freq,
|
|||
|
||||
list_replace_rcu(&opp->node, &new_opp->node);
|
||||
mutex_unlock(&dev_opp_list_lock);
|
||||
synchronize_rcu();
|
||||
kfree_rcu(opp, head);
|
||||
|
||||
/* Notify the change of the OPP availability */
|
||||
if (availability_req)
|
||||
|
@ -522,13 +541,10 @@ static int opp_set_availability(struct device *dev, unsigned long freq,
|
|||
srcu_notifier_call_chain(&dev_opp->head, OPP_EVENT_DISABLE,
|
||||
new_opp);
|
||||
|
||||
/* clean up old opp */
|
||||
new_opp = opp;
|
||||
goto out;
|
||||
return 0;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&dev_opp_list_lock);
|
||||
out:
|
||||
kfree(new_opp);
|
||||
return r;
|
||||
}
|
||||
|
@ -552,6 +568,7 @@ int opp_enable(struct device *dev, unsigned long freq)
|
|||
{
|
||||
return opp_set_availability(dev, freq, true);
|
||||
}
|
||||
EXPORT_SYMBOL(opp_enable);
|
||||
|
||||
/**
|
||||
* opp_disable() - Disable a specific OPP
|
||||
|
@ -573,6 +590,7 @@ int opp_disable(struct device *dev, unsigned long freq)
|
|||
{
|
||||
return opp_set_availability(dev, freq, false);
|
||||
}
|
||||
EXPORT_SYMBOL(opp_disable);
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
/**
|
||||
|
|
|
@ -656,14 +656,14 @@ struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq,
|
|||
opp = opp_find_freq_floor(dev, freq);
|
||||
|
||||
/* If not available, use the closest opp */
|
||||
if (opp == ERR_PTR(-ENODEV))
|
||||
if (opp == ERR_PTR(-ERANGE))
|
||||
opp = opp_find_freq_ceil(dev, freq);
|
||||
} else {
|
||||
/* The freq is an lower bound. opp should be higher */
|
||||
opp = opp_find_freq_ceil(dev, freq);
|
||||
|
||||
/* If not available, use the closest opp */
|
||||
if (opp == ERR_PTR(-ENODEV))
|
||||
if (opp == ERR_PTR(-ERANGE))
|
||||
opp = opp_find_freq_floor(dev, freq);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue