clk: qcom: Add rcg ops to return floor value closest to the requested rate
authorRajendra Nayak <rnayak@codeaurora.org>
Mon, 21 Nov 2016 06:37:11 +0000 (12:07 +0530)
committerStephen Boyd <sboyd@codeaurora.org>
Wed, 23 Nov 2016 19:00:05 +0000 (11:00 -0800)
The default behaviour with clk_rcg2_ops is for the
clk_round_rate()/clk_set_rate() to return/set a ceil clock
rate closest to the requested rate by looking up the corresponding
frequency table.
However, we do have some instances (mainly sdcc on various platforms)
of clients expecting a clk_set_rate() to set a floor value instead.
Add a new clk_rcg2_floor_ops to handle this for such specific
rcg instances

Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
Signed-off-by: Ritesh Harjani <riteshh@codeaurora.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
drivers/clk/qcom/clk-rcg.h
drivers/clk/qcom/clk-rcg2.c
drivers/clk/qcom/common.c
drivers/clk/qcom/common.h

index b904c335cda4275945351e3c772aa171a3c11d32..1b3e8d265bdb02c08c96c7cfecfb3ce8e401de60 100644 (file)
@@ -173,6 +173,7 @@ struct clk_rcg2 {
 #define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr)
 
 extern const struct clk_ops clk_rcg2_ops;
+extern const struct clk_ops clk_rcg2_floor_ops;
 extern const struct clk_ops clk_rcg2_shared_ops;
 extern const struct clk_ops clk_edp_pixel_ops;
 extern const struct clk_ops clk_byte_ops;
index a071bba8018c40424d4d7f02ea80c9622c4d1b26..1a0985ae20d2e34a2f4588784bfd70ba2f66050f 100644 (file)
 #define N_REG                  0xc
 #define D_REG                  0x10
 
+enum freq_policy {
+       FLOOR,
+       CEIL,
+};
+
 static int clk_rcg2_is_enabled(struct clk_hw *hw)
 {
        struct clk_rcg2 *rcg = to_clk_rcg2(hw);
@@ -176,15 +181,26 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
        return calc_rate(parent_rate, m, n, mode, hid_div);
 }
 
-static int _freq_tbl_determine_rate(struct clk_hw *hw,
-               const struct freq_tbl *f, struct clk_rate_request *req)
+static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
+                                   struct clk_rate_request *req,
+                                   enum freq_policy policy)
 {
        unsigned long clk_flags, rate = req->rate;
        struct clk_hw *p;
        struct clk_rcg2 *rcg = to_clk_rcg2(hw);
        int index;
 
-       f = qcom_find_freq(f, rate);
+       switch (policy) {
+       case FLOOR:
+               f = qcom_find_freq_floor(f, rate);
+               break;
+       case CEIL:
+               f = qcom_find_freq(f, rate);
+               break;
+       default:
+               return -EINVAL;
+       };
+
        if (!f)
                return -EINVAL;
 
@@ -221,7 +237,15 @@ static int clk_rcg2_determine_rate(struct clk_hw *hw,
 {
        struct clk_rcg2 *rcg = to_clk_rcg2(hw);
 
-       return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req);
+       return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, CEIL);
+}
+
+static int clk_rcg2_determine_floor_rate(struct clk_hw *hw,
+                                        struct clk_rate_request *req)
+{
+       struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+
+       return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, FLOOR);
 }
 
 static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
@@ -265,12 +289,23 @@ static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
        return update_config(rcg);
 }
 
-static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
+static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
+                              enum freq_policy policy)
 {
        struct clk_rcg2 *rcg = to_clk_rcg2(hw);
        const struct freq_tbl *f;
 
-       f = qcom_find_freq(rcg->freq_tbl, rate);
+       switch (policy) {
+       case FLOOR:
+               f = qcom_find_freq_floor(rcg->freq_tbl, rate);
+               break;
+       case CEIL:
+               f = qcom_find_freq(rcg->freq_tbl, rate);
+               break;
+       default:
+               return -EINVAL;
+       };
+
        if (!f)
                return -EINVAL;
 
@@ -280,13 +315,25 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
 static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
                            unsigned long parent_rate)
 {
-       return __clk_rcg2_set_rate(hw, rate);
+       return __clk_rcg2_set_rate(hw, rate, CEIL);
+}
+
+static int clk_rcg2_set_floor_rate(struct clk_hw *hw, unsigned long rate,
+                                  unsigned long parent_rate)
+{
+       return __clk_rcg2_set_rate(hw, rate, FLOOR);
 }
 
 static int clk_rcg2_set_rate_and_parent(struct clk_hw *hw,
                unsigned long rate, unsigned long parent_rate, u8 index)
 {
-       return __clk_rcg2_set_rate(hw, rate);
+       return __clk_rcg2_set_rate(hw, rate, CEIL);
+}
+
+static int clk_rcg2_set_floor_rate_and_parent(struct clk_hw *hw,
+               unsigned long rate, unsigned long parent_rate, u8 index)
+{
+       return __clk_rcg2_set_rate(hw, rate, FLOOR);
 }
 
 const struct clk_ops clk_rcg2_ops = {
@@ -300,6 +347,17 @@ const struct clk_ops clk_rcg2_ops = {
 };
 EXPORT_SYMBOL_GPL(clk_rcg2_ops);
 
+const struct clk_ops clk_rcg2_floor_ops = {
+       .is_enabled = clk_rcg2_is_enabled,
+       .get_parent = clk_rcg2_get_parent,
+       .set_parent = clk_rcg2_set_parent,
+       .recalc_rate = clk_rcg2_recalc_rate,
+       .determine_rate = clk_rcg2_determine_floor_rate,
+       .set_rate = clk_rcg2_set_floor_rate,
+       .set_rate_and_parent = clk_rcg2_set_floor_rate_and_parent,
+};
+EXPORT_SYMBOL_GPL(clk_rcg2_floor_ops);
+
 static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate)
 {
        struct clk_rcg2 *rcg = to_clk_rcg2(hw);
@@ -323,7 +381,7 @@ static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate)
                pr_err("%s: RCG did not turn on\n", name);
 
        /* set clock rate */
-       ret = __clk_rcg2_set_rate(hw, rate);
+       ret = __clk_rcg2_set_rate(hw, rate, CEIL);
        if (ret)
                return ret;
 
index 94569f461ba7634bf3bd28d17ba1e9c4580d6dee..cfab7b400381ad467f39d9628930745f6345cce4 100644 (file)
@@ -46,6 +46,22 @@ struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate)
 }
 EXPORT_SYMBOL_GPL(qcom_find_freq);
 
+const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,
+                                           unsigned long rate)
+{
+       const struct freq_tbl *best = NULL;
+
+       for ( ; f->freq; f++) {
+               if (rate >= f->freq)
+                       best = f;
+               else
+                       break;
+       }
+
+       return best;
+}
+EXPORT_SYMBOL_GPL(qcom_find_freq_floor);
+
 int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map, u8 src)
 {
        int i, num_parents = clk_hw_get_num_parents(hw);
index 9fb5b8e890718bf99694d30ae5c2f38eb474250d..23c1927669bac89273c39ee1b698ddc31890e5e7 100644 (file)
@@ -41,6 +41,8 @@ struct qcom_cc_desc {
 
 extern const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f,
                                             unsigned long rate);
+extern const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,
+                                                  unsigned long rate);
 extern void
 qcom_pll_set_fsm_mode(struct regmap *m, u32 reg, u8 bias_count, u8 lock_count);
 extern int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map,