This patch add new clk_round_phase API for drivers to
know what the actual phase the clk will provide. Also, add
round_rate and determine_rate callback into clk_ops for clk-driver
to do that. By default, we make min_phase and max_phase to be
0 and 360 respectively.

Signed-off-by: Shawn Lin <[email protected]>

---

 drivers/clk/clk.c            | 138 +++++++++++++++++++++++++++++++++++++++----
 include/linux/clk-provider.h |  20 +++++++
 include/linux/clk.h          |  23 ++++++++
 3 files changed, 171 insertions(+), 10 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 549fdb8..3a8407c 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -64,6 +64,8 @@ struct clk_core {
        unsigned long           max_rate;
        unsigned long           accuracy;
        int                     phase;
+       unsigned long           min_phase;
+       unsigned long           max_phase;
        struct hlist_head       children;
        struct hlist_node       child_node;
        struct hlist_head       clks;
@@ -522,18 +524,27 @@ struct clk *__clk_lookup(const char *name)
 
 static void clk_core_get_boundaries(struct clk_core *core,
                                    unsigned long *min_rate,
-                                   unsigned long *max_rate)
+                                   unsigned long *max_rate,
+                                   unsigned long *min_phase,
+                                   unsigned long *max_phase)
 {
        struct clk *clk_user;
 
-       *min_rate = core->min_rate;
-       *max_rate = core->max_rate;
+       if (min_rate && max_rate) {
+               *min_rate = core->min_rate;
+               *max_rate = core->max_rate;
 
-       hlist_for_each_entry(clk_user, &core->clks, clks_node)
-               *min_rate = max(*min_rate, clk_user->min_rate);
+               hlist_for_each_entry(clk_user, &core->clks, clks_node)
+                       *min_rate = max(*min_rate, clk_user->min_rate);
 
-       hlist_for_each_entry(clk_user, &core->clks, clks_node)
-               *max_rate = min(*max_rate, clk_user->max_rate);
+               hlist_for_each_entry(clk_user, &core->clks, clks_node)
+                       *max_rate = min(*max_rate, clk_user->max_rate);
+       }
+
+       if (min_phase && max_phase) {
+               *min_phase = core->min_phase;
+               *max_phase = core->max_phase;
+       }
 }
 
 void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate,
@@ -845,7 +856,8 @@ unsigned long clk_hw_round_rate(struct clk_hw *hw, unsigned 
long rate)
        int ret;
        struct clk_rate_request req;
 
-       clk_core_get_boundaries(hw->core, &req.min_rate, &req.max_rate);
+       clk_core_get_boundaries(hw->core, &req.min_rate, &req.max_rate,
+                               NULL, NULL);
        req.rate = rate;
 
        ret = clk_core_round_rate_nolock(hw->core, &req);
@@ -875,7 +887,8 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
 
        clk_prepare_lock();
 
-       clk_core_get_boundaries(clk->core, &req.min_rate, &req.max_rate);
+       clk_core_get_boundaries(clk->core, &req.min_rate, &req.max_rate,
+                               NULL, NULL);
        req.rate = rate;
 
        ret = clk_core_round_rate_nolock(clk->core, &req);
@@ -888,6 +901,106 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
 }
 EXPORT_SYMBOL_GPL(clk_round_rate);
 
+static int clk_core_round_phase_nolock(struct clk_core *core,
+                                     struct clk_phase_request *req)
+{
+       long phase;
+
+       lockdep_assert_held(&prepare_lock);
+
+       if (!core)
+               return 0;
+
+       /* sanity check  */
+       req->phase %= 360;
+       if (req->phase < 0)
+               req->phase += 360;
+
+       if (core->ops->determine_phase) {
+               return core->ops->determine_phase(core->hw, req);
+       } else if (core->ops->round_phase) {
+               phase = core->ops->round_phase(core->hw, req->phase);
+               if (phase < 0)
+                       return phase;
+
+               req->phase = phase;
+       }
+
+       return 0;
+}
+
+/**
+ * __clk_determine_phase - get the closest phase actually supported by a clock
+ * @hw: determine the phase of this clock
+ * @phase: target rate
+ * @min_phase: returned phase must be greater than this phase
+ * @max_phase: returned phase must be less than this phase
+ *
+ * Useful for clk_ops such as .set_phase and .determine_phase.
+ */
+int __clk_determine_phase(struct clk_hw *hw, struct clk_phase_request *req)
+{
+       if (!hw) {
+               req->phase = 0;
+               return 0;
+       }
+
+       return clk_core_round_phase_nolock(hw->core, req);
+}
+EXPORT_SYMBOL_GPL(__clk_determine_phase);
+
+unsigned long clk_hw_round_phase(struct clk_hw *hw, int phase)
+{
+       int ret;
+       struct clk_phase_request req;
+
+       clk_core_get_boundaries(hw->core, NULL, NULL,
+                               &req.min_phase, &req.max_phase);
+
+       req.phase = phase;
+
+       ret = clk_core_round_phase_nolock(hw->core, &req);
+       if (ret)
+               return 0;
+
+       return req.phase;
+}
+EXPORT_SYMBOL_GPL(clk_hw_round_phase);
+
+/**
+ * clk_round_phase - round the given phase for a clk
+ * @clk: the clk for which we are rounding a phase
+ * @phase: the phase which is to be rounded
+ *
+ * Takes in a phase as input and rounds it to a phase that the clk can actually
+ * use which is then returned.  If clk doesn't support round_phase operation
+ * then the requested phase is returned.
+ */
+long clk_round_phase(struct clk *clk, int phase)
+{
+       struct clk_phase_request req;
+       int ret;
+
+       if (!clk)
+               return 0;
+
+       clk_prepare_lock();
+
+       clk_core_get_boundaries(clk->core, NULL, NULL,
+                               &req.min_phase, &req.max_phase);
+       req.phase = phase;
+
+       ret = clk_core_round_phase_nolock(clk->core, &req);
+       clk_prepare_unlock();
+
+       if (ret)
+               return ret;
+
+       return req.phase;
+}
+EXPORT_SYMBOL_GPL(clk_round_phase);
+
+
 /**
  * __clk_notify - call clk notifier chain
  * @core: clk that is changing rate
@@ -1312,7 +1425,8 @@ static struct clk_core *clk_calc_new_rates(struct 
clk_core *core,
        if (parent)
                best_parent_rate = parent->rate;
 
-       clk_core_get_boundaries(core, &min_rate, &max_rate);
+       clk_core_get_boundaries(core, &min_rate, &max_rate,
+                               NULL, NULL);
 
        /* find the closest rate and parent clk/rate */
        if (core->ops->determine_rate) {
@@ -2443,6 +2557,10 @@ static int __clk_init(struct device *dev, struct clk 
*clk_user)
        else
                core->phase = 0;
 
+       /* Set phase range from 0 to 360 by default */
+       core->min_phase = 0;
+       core->max_phase = 360;
+
        /*
         * Set clk's rate.  The preferred method is to use .recalc_rate.  For
         * simple clocks and lazy developers the default fallback is to use the
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 1143e38..f7e58ae 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -61,6 +61,23 @@ struct clk_rate_request {
 };
 
 /**
+ * struct clk_phase_request - Structure encoding the clk constraints that
+ * a clock user might require.
+ *
+ * @phase:             Requested clock phase. This field will be adjusted by
+ *                     clock drivers according to hardware capabilities.
+ * @min_phase:         Minimum phase imposed by clk users.
+ * @max_phase:         Maximum phase imposed by clk users.
+ *
+ */
+struct clk_phase_request {
+       int phase;
+       unsigned long min_phase;
+       unsigned long max_phase;
+};
+
+
+/**
  * struct clk_ops -  Callback operations for hardware clocks; these are to
  * be provided by the clock implementation, and will be called by drivers
  * through the clk_* api.
@@ -212,6 +229,9 @@ struct clk_ops {
                                           unsigned long parent_accuracy);
        int             (*get_phase)(struct clk_hw *hw);
        int             (*set_phase)(struct clk_hw *hw, int degrees);
+       int             (*round_phase)(struct clk_hw *hw, int degrees);
+       int             (*determine_phase)(struct clk_hw *hw,
+                                         struct clk_phase_request *req);
        void            (*init)(struct clk_hw *hw);
        int             (*debug_init)(struct clk_hw *hw, struct dentry *dentry);
 };
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 0df4a51..105cae0 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -325,6 +325,29 @@ void devm_clk_put(struct device *dev, struct clk *clk);
 long clk_round_rate(struct clk *clk, unsigned long rate);
 
 /**
+ * clk_round_phase - adjust a phase to the exact phase a clock can provide
+ * @clk: clock source
+ * @phase: desired clock phase in degrees
+ *
+ * This answers the question "if I were to pass @phase to clk_set_phase(),
+ * what clock phase would I end up with?" without changing the hardware
+ * in any way.  In other words:
+ *
+ *   phase = clk_round_phase(clk, p);
+ *
+ * and:
+ *
+ *   clk_set_phase(clk, p);
+ *   phase = clk_get_phase(clk);
+ *
+ * are equivalent except the former does not modify the clock hardware
+ * in any way.
+ *
+ * Returns rounded clock phase in degrees, or negative errno.
+ */
+long clk_round_phase(struct clk *clk, int phase);
+
+/**
  * clk_set_rate - set the clock rate for a clock source
  * @clk: clock source
  * @rate: desired clock rate in Hz
-- 
2.3.7


Reply via email to