Merge tag 'pm+acpi-4.4-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[firefly-linux-kernel-4.4.55.git] / drivers / base / power / opp / core.c
index 252706d6f60b9c08ceb73851a5d59676c48e50d4..b8e76f75073b47a945b695506bb68176c586a161 100644 (file)
@@ -81,14 +81,18 @@ static struct device_opp *_managed_opp(const struct device_node *np)
  * Return: pointer to 'struct device_opp' if found, otherwise -ENODEV or
  * -EINVAL based on type of error.
  *
- * Locking: This function must be called under rcu_read_lock(). device_opp
- * is a RCU protected pointer. This means that device_opp is valid as long
- * as we are under RCU lock.
+ * Locking: For readers, this function must be called under rcu_read_lock().
+ * device_opp is a RCU protected pointer, which means that device_opp is valid
+ * as long as we are under RCU lock.
+ *
+ * For Writers, this function must be called with dev_opp_list_lock held.
  */
 struct device_opp *_find_device_opp(struct device *dev)
 {
        struct device_opp *dev_opp;
 
+       opp_rcu_lockdep_assert();
+
        if (IS_ERR_OR_NULL(dev)) {
                pr_err("%s: Invalid parameters\n", __func__);
                return ERR_PTR(-EINVAL);
@@ -102,7 +106,7 @@ struct device_opp *_find_device_opp(struct device *dev)
 }
 
 /**
- * dev_pm_opp_get_voltage() - Gets the voltage corresponding to an available opp
+ * dev_pm_opp_get_voltage() - Gets the voltage corresponding to an opp
  * @opp:       opp for which voltage has to be returned for
  *
  * Return: voltage in micro volt corresponding to the opp, else
@@ -124,7 +128,7 @@ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
        opp_rcu_lockdep_assert();
 
        tmp_opp = rcu_dereference(opp);
-       if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available)
+       if (IS_ERR_OR_NULL(tmp_opp))
                pr_err("%s: Invalid parameters\n", __func__);
        else
                v = tmp_opp->u_volt;
@@ -778,10 +782,17 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev)
        u32 val;
        int count, ret;
 
-       count = of_property_count_u32_elems(opp->np, "opp-microvolt");
-       if (!count)
+       /* Missing property isn't a problem, but an invalid entry is */
+       if (!of_find_property(opp->np, "opp-microvolt", NULL))
                return 0;
 
+       count = of_property_count_u32_elems(opp->np, "opp-microvolt");
+       if (count < 0) {
+               dev_err(dev, "%s: Invalid opp-microvolt property (%d)\n",
+                       __func__, count);
+               return count;
+       }
+
        /* There can be one or three elements here */
        if (count != 1 && count != 3) {
                dev_err(dev, "%s: Invalid number of elements in opp-microvolt property (%d)\n",
@@ -949,7 +960,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_add);
  * share a common logic which is isolated here.
  *
  * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the
- * copy operation, returns 0 if no modifcation was done OR modification was
+ * copy operation, returns 0 if no modification was done OR modification was
  * successful.
  *
  * Locking: The internal device_opp and opp structures are RCU protected.
@@ -1037,7 +1048,7 @@ unlock:
  * mutex locking or synchronize_rcu() blocking calls cannot be used.
  *
  * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the
- * copy operation, returns 0 if no modifcation was done OR modification was
+ * copy operation, returns 0 if no modification was done OR modification was
  * successful.
  */
 int dev_pm_opp_enable(struct device *dev, unsigned long freq)
@@ -1063,7 +1074,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_enable);
  * mutex locking or synchronize_rcu() blocking calls cannot be used.
  *
  * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the
- * copy operation, returns 0 if no modifcation was done OR modification was
+ * copy operation, returns 0 if no modification was done OR modification was
  * successful.
  */
 int dev_pm_opp_disable(struct device *dev, unsigned long freq)
@@ -1168,13 +1179,17 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
        struct device_opp *dev_opp;
        int ret = 0, count = 0;
 
+       mutex_lock(&dev_opp_list_lock);
+
        dev_opp = _managed_opp(opp_np);
        if (dev_opp) {
                /* OPPs are already managed */
                if (!_add_list_dev(dev, dev_opp))
                        ret = -ENOMEM;
+               mutex_unlock(&dev_opp_list_lock);
                return ret;
        }
+       mutex_unlock(&dev_opp_list_lock);
 
        /* We have opp-list node now, iterate over it and add OPPs */
        for_each_available_child_of_node(opp_np, np) {
@@ -1192,15 +1207,20 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
        if (WARN_ON(!count))
                return -ENOENT;
 
+       mutex_lock(&dev_opp_list_lock);
+
        dev_opp = _find_device_opp(dev);
        if (WARN_ON(IS_ERR(dev_opp))) {
                ret = PTR_ERR(dev_opp);
+               mutex_unlock(&dev_opp_list_lock);
                goto free_table;
        }
 
        dev_opp->np = opp_np;
        dev_opp->shared_opp = of_property_read_bool(opp_np, "opp-shared");
 
+       mutex_unlock(&dev_opp_list_lock);
+
        return 0;
 
 free_table: