clk: defer clk_gets on orphan clocks
[firefly-linux-kernel-4.4.55.git] / drivers / clk / clk.c
index f13c3f4228d4d51d5649c3705fd565b4aac82913..f66b24e9e18e5c00b4854b4bfd861029d075cda4 100644 (file)
@@ -1423,6 +1423,9 @@ static struct clk_core *clk_propagate_rate_change(struct clk_core *core,
        return fail_clk;
 }
 
+static int clk_core_set_rate_nolock(struct clk_core *core,
+                                   unsigned long req_rate);
+
 /*
  * walk down a subtree and set the new rates notifying the rate
  * change on the way
@@ -1443,6 +1446,15 @@ static void clk_change_rate(struct clk_core *core)
        else if (core->parent)
                best_parent_rate = core->parent->rate;
 
+       if (core->flags & CLK_SET_RATE_UNGATE) {
+               unsigned long flags;
+
+               clk_core_prepare(core);
+               flags = clk_enable_lock();
+               clk_core_enable(core);
+               clk_enable_unlock(flags);
+       }
+
        if (core->new_parent && core->new_parent != core->parent) {
                old_parent = __clk_set_parent_before(core, core->new_parent);
                trace_clk_set_parent(core, core->new_parent);
@@ -1469,6 +1481,15 @@ static void clk_change_rate(struct clk_core *core)
 
        core->rate = clk_recalc(core, best_parent_rate);
 
+       if (core->flags & CLK_SET_RATE_UNGATE) {
+               unsigned long flags;
+
+               flags = clk_enable_lock();
+               clk_core_disable(core);
+               clk_enable_unlock(flags);
+               clk_core_unprepare(core);
+       }
+
        if (core->notifier_count && old_rate != core->rate)
                __clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate);
 
@@ -1489,6 +1510,12 @@ static void clk_change_rate(struct clk_core *core)
        /* handle the new child who might not be in core->children yet */
        if (core->new_child)
                clk_change_rate(core->new_child);
+
+       /* handle a changed clock that needs to readjust its rate */
+       if (core->flags & CLK_KEEP_REQ_RATE && core->req_rate
+                                           && core->new_rate != old_rate
+                                           && core->new_rate != core->req_rate)
+               clk_core_set_rate_nolock(core, core->req_rate);
 }
 
 static int clk_core_set_rate_nolock(struct clk_core *core,
@@ -1502,8 +1529,10 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
                return 0;
 
        /* bail early if nothing to do */
-       if (rate == clk_core_get_rate_nolock(core))
+       if (rate == clk_core_get_rate_nolock(core)) {
+               core->req_rate = req_rate;
                return 0;
+       }
 
        if ((core->flags & CLK_SET_RATE_GATE) && core->prepare_count)
                return -EBUSY;
@@ -1522,11 +1551,11 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
                return -EBUSY;
        }
 
+       core->req_rate = req_rate;
+
        /* change the rates */
        clk_change_rate(top);
 
-       core->req_rate = req_rate;
-
        return ret;
 }
 
@@ -1594,9 +1623,14 @@ int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max)
        clk_prepare_lock();
 
        if (min != clk->min_rate || max != clk->max_rate) {
+               unsigned long rate = clk->core->req_rate;
+
+               if (!rate)
+                       rate = clk->core->rate;
+
                clk->min_rate = min;
                clk->max_rate = max;
-               ret = clk_core_set_rate_nolock(clk->core, clk->core->req_rate);
+               ret = clk_core_set_rate_nolock(clk->core, rate);
        }
 
        clk_prepare_unlock();
@@ -1884,6 +1918,10 @@ int clk_set_phase(struct clk *clk, int degrees)
 
        clk_prepare_lock();
 
+       /* bail early if nothing to do */
+       if (degrees == clk->core->phase)
+               goto out;
+
        trace_clk_set_phase(clk->core, degrees);
 
        if (clk->core->ops->set_phase)
@@ -1894,6 +1932,7 @@ int clk_set_phase(struct clk *clk, int degrees)
        if (!ret)
                clk->core->phase = degrees;
 
+out:
        clk_prepare_unlock();
 
        return ret;
@@ -2433,7 +2472,7 @@ static int __clk_init(struct device *dev, struct clk *clk_user)
                rate = core->parent->rate;
        else
                rate = 0;
-       core->rate = core->req_rate = rate;
+       core->rate = rate;
 
        /*
         * walk the list of orphan clocks and reparent any that are children of
@@ -2476,15 +2515,11 @@ out:
        return ret;
 }
 
-struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id,
-                            const char *con_id)
+static struct clk *clk_hw_create_clk(struct clk_hw *hw, const char *dev_id,
+                                    const char *con_id)
 {
        struct clk *clk;
 
-       /* This is to allow this function to be chained to others */
-       if (!hw || IS_ERR(hw))
-               return (struct clk *) hw;
-
        clk = kzalloc(sizeof(*clk), GFP_KERNEL);
        if (!clk)
                return ERR_PTR(-ENOMEM);
@@ -2501,6 +2536,19 @@ struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id,
        return clk;
 }
 
+struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id,
+                            const char *con_id, bool with_orphans)
+{
+       /* This is to allow this function to be chained to others */
+       if (!hw || IS_ERR(hw))
+               return (struct clk *) hw;
+
+       if (hw->core->orphan && !with_orphans)
+               return ERR_PTR(-EPROBE_DEFER);
+
+       return clk_hw_create_clk(hw, dev_id, con_id);
+}
+
 void __clk_free_clk(struct clk *clk)
 {
        clk_prepare_lock();
@@ -2569,7 +2617,7 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
 
        INIT_HLIST_HEAD(&core->clks);
 
-       hw->clk = __clk_create_clk(hw, NULL, NULL);
+       hw->clk = clk_hw_create_clk(hw, NULL, NULL);
        if (IS_ERR(hw->clk)) {
                ret = PTR_ERR(hw->clk);
                goto fail_parent_names_copy;
@@ -2771,6 +2819,7 @@ int __clk_get(struct clk *clk)
 
 void __clk_put(struct clk *clk)
 {
+       unsigned long rate;
        struct module *owner;
 
        if (!clk || WARN_ON_ONCE(IS_ERR(clk)))
@@ -2779,9 +2828,13 @@ void __clk_put(struct clk *clk)
        clk_prepare_lock();
 
        hlist_del(&clk->clks_node);
-       if (clk->min_rate > clk->core->req_rate ||
-           clk->max_rate < clk->core->req_rate)
-               clk_core_set_rate_nolock(clk->core, clk->core->req_rate);
+
+       rate = clk->core->req_rate;
+       if (!rate)
+               rate = clk->core->rate;
+
+       if (clk->min_rate > rate || clk->max_rate < rate)
+               clk_core_set_rate_nolock(clk->core, rate);
 
        owner = clk->core->owner;
        kref_put(&clk->core->ref, __clk_release);
@@ -3002,7 +3055,8 @@ void of_clk_del_provider(struct device_node *np)
 EXPORT_SYMBOL_GPL(of_clk_del_provider);
 
 struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec,
-                                      const char *dev_id, const char *con_id)
+                                      const char *dev_id, const char *con_id,
+                                      bool with_orphans)
 {
        struct of_clk_provider *provider;
        struct clk *clk = ERR_PTR(-EPROBE_DEFER);
@@ -3017,7 +3071,7 @@ struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec,
                        clk = provider->get(clkspec, provider->data);
                if (!IS_ERR(clk)) {
                        clk = __clk_create_clk(__clk_get_hw(clk), dev_id,
-                                              con_id);
+                                              con_id, with_orphans);
 
                        if (!IS_ERR(clk) && !__clk_get(clk)) {
                                __clk_free_clk(clk);
@@ -3042,7 +3096,25 @@ struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec,
  */
 struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
 {
-       return __of_clk_get_from_provider(clkspec, NULL, __func__);
+       return __of_clk_get_from_provider(clkspec, NULL, __func__, false);
+}
+
+/**
+ * of_clk_get_from_provider_with_orphans() - Lookup clock from a clock provider
+ * @clkspec: pointer to a clock specifier data structure
+ *
+ * This function looks up a struct clk from the registered list of clock
+ * providers, an input is a clock specifier data structure as returned
+ * from the of_parse_phandle_with_args() function call.
+ *
+ * The difference to of_clk_get_from_provider() is that this function will
+ * also successfully lookup orphan-clocks, as it in some cases may be
+ * necessary to access such orphan-clocks as well.
+ */
+struct clk *
+of_clk_get_from_provider_with_orphans(struct of_phandle_args *clkspec)
+{
+       return __of_clk_get_from_provider(clkspec, NULL, __func__, true);
 }
 
 int of_clk_get_parent_count(struct device_node *np)