soc: rockchip: add cpuinfo support
[firefly-linux-kernel-4.4.55.git] / drivers / clk / clk.c
index f13c3f4228d4d51d5649c3705fd565b4aac82913..ba7d8e3e3db58035b2a199b189a94a6277fbf6df 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
@@ -2771,6 +2810,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 +2819,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);