]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/opp/of.c
OPP: Don't remove dynamic OPPs from _dev_pm_opp_remove_table()
[linux.git] / drivers / opp / of.c
index 86222586f27b8df879cdd2d0df914740e7f52f6e..861cc75de329bc840541d28b40334b893e926f25 100644 (file)
 
 #include "opp.h"
 
+/*
+ * Returns opp descriptor node for a device node, caller must
+ * do of_node_put().
+ */
+static struct device_node *_opp_of_get_opp_desc_node(struct device_node *np,
+                                                    int index)
+{
+       /* "operating-points-v2" can be an array for power domain providers */
+       return of_parse_phandle(np, "operating-points-v2", index);
+}
+
+/* Returns opp descriptor node for a device, caller must do of_node_put() */
+struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev)
+{
+       return _opp_of_get_opp_desc_node(dev->of_node, 0);
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_opp_desc_node);
+
 static struct opp_table *_managed_opp(const struct device_node *np)
 {
        struct opp_table *opp_table, *managed_table = NULL;
@@ -52,24 +70,40 @@ static struct opp_table *_managed_opp(const struct device_node *np)
        return managed_table;
 }
 
-void _of_init_opp_table(struct opp_table *opp_table, struct device *dev)
+void _of_init_opp_table(struct opp_table *opp_table, struct device *dev,
+                       int index)
 {
-       struct device_node *np;
+       struct device_node *np, *opp_np;
+       u32 val;
 
        /*
         * Only required for backward compatibility with v1 bindings, but isn't
         * harmful for other cases. And so we do it unconditionally.
         */
        np = of_node_get(dev->of_node);
-       if (np) {
-               u32 val;
-
-               if (!of_property_read_u32(np, "clock-latency", &val))
-                       opp_table->clock_latency_ns_max = val;
-               of_property_read_u32(np, "voltage-tolerance",
-                                    &opp_table->voltage_tolerance_v1);
-               of_node_put(np);
-       }
+       if (!np)
+               return;
+
+       if (!of_property_read_u32(np, "clock-latency", &val))
+               opp_table->clock_latency_ns_max = val;
+       of_property_read_u32(np, "voltage-tolerance",
+                            &opp_table->voltage_tolerance_v1);
+
+       /* Get OPP table node */
+       opp_np = _opp_of_get_opp_desc_node(np, index);
+       of_node_put(np);
+
+       if (!opp_np)
+               return;
+
+       if (of_property_read_bool(opp_np, "opp-shared"))
+               opp_table->shared_opp = OPP_TABLE_ACCESS_SHARED;
+       else
+               opp_table->shared_opp = OPP_TABLE_ACCESS_EXCLUSIVE;
+
+       opp_table->np = opp_np;
+
+       of_node_put(opp_np);
 }
 
 static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table,
@@ -245,26 +279,10 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
  */
 void dev_pm_opp_of_remove_table(struct device *dev)
 {
-       _dev_pm_opp_find_and_remove_table(dev, false);
+       _dev_pm_opp_find_and_remove_table(dev);
 }
 EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table);
 
-/* Returns opp descriptor node for a device node, caller must
- * do of_node_put() */
-static struct device_node *_opp_of_get_opp_desc_node(struct device_node *np,
-                                                    int index)
-{
-       /* "operating-points-v2" can be an array for power domain providers */
-       return of_parse_phandle(np, "operating-points-v2", index);
-}
-
-/* Returns opp descriptor node for a device, caller must do of_node_put() */
-struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev)
-{
-       return _opp_of_get_opp_desc_node(dev->of_node, 0);
-}
-EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_opp_desc_node);
-
 /**
  * _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings)
  * @opp_table: OPP table
@@ -378,7 +396,8 @@ static int _opp_add_static_v2(struct opp_table *opp_table, struct device *dev,
 }
 
 /* Initializes OPP tables based on new bindings */
-static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
+static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np,
+                               int index)
 {
        struct device_node *np;
        struct opp_table *opp_table;
@@ -390,13 +409,21 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
                /* OPPs are already managed */
                if (!_add_opp_dev(dev, opp_table))
                        ret = -ENOMEM;
+               else if (!opp_table->parsed_static_opps)
+                       goto initialize_static_opps;
+               else
+                       kref_get(&opp_table->list_kref);
+
                goto put_opp_table;
        }
 
-       opp_table = dev_pm_opp_get_opp_table(dev);
+       opp_table = dev_pm_opp_get_opp_table_indexed(dev, index);
        if (!opp_table)
                return -ENOMEM;
 
+initialize_static_opps:
+       kref_init(&opp_table->list_kref);
+
        /* We have opp-table node now, iterate over it and add OPPs */
        for_each_available_child_of_node(opp_np, np) {
                count++;
@@ -405,7 +432,7 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
                if (ret) {
                        dev_err(dev, "%s: Failed to add OPP, %d\n", __func__,
                                ret);
-                       _dev_pm_opp_remove_table(opp_table, dev, false);
+                       _dev_pm_opp_remove_table(opp_table, dev);
                        of_node_put(np);
                        goto put_opp_table;
                }
@@ -414,6 +441,7 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
        /* There should be one of more OPP defined */
        if (WARN_ON(!count)) {
                ret = -ENOENT;
+               _put_opp_list_kref(opp_table);
                goto put_opp_table;
        }
 
@@ -425,18 +453,14 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
                dev_err(dev, "Not all nodes have performance state set (%d: %d)\n",
                        count, pstate_count);
                ret = -ENOENT;
-               _dev_pm_opp_remove_table(opp_table, dev, false);
+               _dev_pm_opp_remove_table(opp_table, dev);
                goto put_opp_table;
        }
 
        if (pstate_count)
                opp_table->genpd_performance_state = true;
 
-       opp_table->np = opp_np;
-       if (of_property_read_bool(opp_np, "opp-shared"))
-               opp_table->shared_opp = OPP_TABLE_ACCESS_SHARED;
-       else
-               opp_table->shared_opp = OPP_TABLE_ACCESS_EXCLUSIVE;
+       opp_table->parsed_static_opps = true;
 
 put_opp_table:
        dev_pm_opp_put_opp_table(opp_table);
@@ -472,6 +496,8 @@ static int _of_add_opp_table_v1(struct device *dev)
        if (!opp_table)
                return -ENOMEM;
 
+       kref_init(&opp_table->list_kref);
+
        val = prop->value;
        while (nr) {
                unsigned long freq = be32_to_cpup(val++) * 1000;
@@ -481,7 +507,7 @@ static int _of_add_opp_table_v1(struct device *dev)
                if (ret) {
                        dev_err(dev, "%s: Failed to add OPP %ld (%d)\n",
                                __func__, freq, ret);
-                       _dev_pm_opp_remove_table(opp_table, dev, false);
+                       _dev_pm_opp_remove_table(opp_table, dev);
                        break;
                }
                nr -= 2;
@@ -526,7 +552,7 @@ int dev_pm_opp_of_add_table(struct device *dev)
                return _of_add_opp_table_v1(dev);
        }
 
-       ret = _of_add_opp_table_v2(dev, opp_np);
+       ret = _of_add_opp_table_v2(dev, opp_np, 0);
        of_node_put(opp_np);
 
        return ret;
@@ -574,7 +600,7 @@ int dev_pm_opp_of_add_table_indexed(struct device *dev, int index)
                return -ENODEV;
        }
 
-       ret = _of_add_opp_table_v2(dev, opp_np);
+       ret = _of_add_opp_table_v2(dev, opp_np, index);
        of_node_put(opp_np);
 
        return ret;
@@ -592,7 +618,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_indexed);
  */
 void dev_pm_opp_of_cpumask_remove_table(const struct cpumask *cpumask)
 {
-       _dev_pm_opp_cpumask_remove_table(cpumask, true, -1);
+       _dev_pm_opp_cpumask_remove_table(cpumask, -1);
 }
 EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_remove_table);
 
@@ -627,7 +653,7 @@ int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask)
                                 __func__, cpu, ret);
 
                        /* Free all other OPPs */
-                       _dev_pm_opp_cpumask_remove_table(cpumask, true, cpu);
+                       _dev_pm_opp_cpumask_remove_table(cpumask, cpu);
                        break;
                }
        }