Add support for the clk_round_rate API to our CCF clock provider.

Signed-off-by: James Kelly <jamespeterke...@gmail.com>
---
 .../clocking-wizard/clk-xlnx-clock-wizard.c        | 107 +++++++++++++++++++++
 1 file changed, 107 insertions(+)

diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c 
b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c
index 8929913045e7..8828dac6faaf 100644
--- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c
+++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c
@@ -83,6 +83,7 @@
 #define WZRD_CLKNAME_IN1       "clk_in1"
 #define WZRD_FLAG_MULTIPLY     BIT(0)
 #define WZRD_FLAG_FRAC         BIT(1)
+#define WZRD_FLAG_ADJUST_MIN   BIT(2)
 
 /*
  * Clock rate constraints extracted from Xilinx data sheets listed below.
@@ -309,16 +310,19 @@ static const struct reg_field clk_wzrd_reconfig     = 
REG_FIELD(0x25C, 0,  1);
  *
  * @hw:                        handle between common and hardware-specific 
interfaces
  * @flags:             hardware specific flags
+ * @ratio_limit:       pointer to divider/multiplier ratio limits
  * @int_field:         pointer to regmap field for integer part
  * @frac_field:                pointer to regmap field for fractional part
  *
  * Flags:
  * WZRD_FLAG_MULTIPLY  Clock is a multiplier rather than a divider
  * WZRD_FLAG_FRAC      Clock ratio can be fractional
+ * WZRD_FLAG_ADJUST_MIN        When clock is fractional minimum ratio 
increases by 1
  */
 struct clk_wzrd_clk_data {
        struct clk_hw                   hw;
        unsigned long                   flags;
+       const struct clk_wzrd_ratio     *ratio_limit;
        struct regmap_field             *int_field;
        struct regmap_field             *frac_field;
 };
@@ -425,6 +429,86 @@ static unsigned long clk_wzrd_recalc_rate(struct clk_hw 
*hw,
        return clk_wzrd_calc_rate(parent_rate, ratio, cwc->flags);
 }
 
+static unsigned long clk_wzrd_calc_ratio(unsigned long parent_rate,
+                                        unsigned long req_rate,
+                                        unsigned long flags)
+{
+       unsigned long long t;
+       unsigned long n, d;
+
+       if (flags & WZRD_FLAG_MULTIPLY) {
+               n = req_rate;
+               d = parent_rate;
+       } else {
+               n = parent_rate;
+               d = req_rate;
+       }
+
+       if (flags & WZRD_FLAG_FRAC) {
+               /* Round at least significant bit */
+               t = (unsigned long long)n << WZRD_FRAC_BITS;
+               return (unsigned long)DIV_ROUND_CLOSEST_ULL(t, d);
+       }
+
+       /* Round at decimal point */
+       t = (unsigned long long)n;
+       return (unsigned long)DIV_ROUND_CLOSEST_ULL(t, d) << WZRD_FRAC_BITS;
+}
+
+static unsigned long clk_wzrd_limit_calc_ratio(struct clk_wzrd_clk_data *cwc,
+                                              unsigned long parent_rate,
+                                              unsigned long req_rate)
+{
+       unsigned long ratio;
+
+       ratio = clk_wzrd_calc_ratio(parent_rate, req_rate, cwc->flags);
+
+       /*
+        * Some fractional multiple/divide hardware cannot do fractional
+        * multiply/divide between the minimum ratio limit and the
+        * minimum ratio limit + 1, as indicated by WZRD_FLAG_ADJUST_MIN.
+        * If we get a ratio in this range we have to recalculate the
+        * ratio so it is not fractional and rounded to an integer.
+        */
+       if (ratio >> WZRD_FRAC_BITS == cwc->ratio_limit->min &&
+           cwc->flags & WZRD_FLAG_ADJUST_MIN && cwc->flags & WZRD_FLAG_FRAC)
+               ratio = clk_wzrd_calc_ratio(parent_rate, req_rate,
+                                           cwc->flags & ~WZRD_FLAG_FRAC);
+
+       ratio = clamp_val(ratio, cwc->ratio_limit->min << WZRD_FRAC_BITS,
+                         cwc->ratio_limit->max << WZRD_FRAC_BITS);
+
+       return ratio;
+}
+
+static inline unsigned long clk_wzrd_round_rate(struct clk_wzrd_clk_data *cwc,
+                                               unsigned long parent_rate,
+                                               unsigned long req_rate)
+{
+       unsigned long ratio = clk_wzrd_limit_calc_ratio(cwc, parent_rate,
+                                                       req_rate);
+       return clk_wzrd_calc_rate(parent_rate, ratio, cwc->flags);
+}
+
+static int clk_wzrd_determine_rate(struct clk_hw *hw,
+                                  struct clk_rate_request *req)
+{
+       struct clk_wzrd_clk_data *cwc = to_clk_wzrd_clk_data(hw);
+
+       if (cwc->flags & WZRD_FLAG_MULTIPLY && req->best_parent_rate == 0)
+               return -EINVAL;
+
+       if (!(cwc->flags & WZRD_FLAG_MULTIPLY) && req->rate == 0)
+               return -EINVAL;
+
+       req->rate = clk_wzrd_round_rate(cwc, req->best_parent_rate, req->rate);
+
+       if (req->rate < req->min_rate || req->rate > req->max_rate)
+               return -EINVAL;
+
+       return 0;
+}
+
 #ifdef CONFIG_DEBUG_FS
 
 static int clk_wzrd_flags_show(struct seq_file *s, void *data)
@@ -436,6 +520,8 @@ static int clk_wzrd_flags_show(struct seq_file *s, void 
*data)
                seq_puts(s, "WZRD_FLAG_MULTIPLY\n");
        if (cwc->flags & WZRD_FLAG_FRAC)
                seq_puts(s, "WZRD_FLAG_FRAC\n");
+       if (cwc->flags & WZRD_FLAG_ADJUST_MIN)
+               seq_puts(s, "WZRD_FLAG_ADJUST_MIN\n");
 
        return 0;
 }
@@ -463,6 +549,16 @@ static int clk_wzrd_debug_init(struct clk_hw *hw, struct 
dentry *dentry)
        struct dentry *d;
        struct clk_wzrd_clk_data *cwc = to_clk_wzrd_clk_data(hw);
 
+       d = debugfs_create_u16("clk_ratio_min", 0444, dentry,
+                              (u16 *)&cwc->ratio_limit->min);
+       if (IS_ERR(d))
+               return PTR_ERR(d);
+
+       d = debugfs_create_u16("clk_ratio_max", 0444, dentry,
+                              (u16 *)&cwc->ratio_limit->max);
+       if (IS_ERR(d))
+               return PTR_ERR(d);
+
        d = debugfs_create_file_unsafe("clk_hw_flags", 0444, dentry, cwc,
                                       &clk_wzrd_flags_fops);
        if (IS_ERR(d))
@@ -479,6 +575,7 @@ static int clk_wzrd_debug_init(struct clk_hw *hw, struct 
dentry *dentry)
 
 static const struct clk_ops clk_wzrd_clk_ops = {
        .recalc_rate = clk_wzrd_recalc_rate,
+       .determine_rate = clk_wzrd_determine_rate,
 #ifdef CONFIG_DEBUG_FS
        .debug_init = clk_wzrd_debug_init,
 #endif
@@ -492,6 +589,7 @@ static int clk_wzrd_register_clk(struct device *dev, const 
char *name,
        struct clk_init_data init;
        const struct reg_field *int_reg_field;
        const struct reg_field *frac_reg_field;
+       enum clk_wzrd_rate rate_constraint;
        struct clk_wzrd_clk_data *cwc;
        const char *parent_name;
        struct clk_wzrd *cw = dev_get_drvdata(dev);
@@ -503,6 +601,7 @@ static int clk_wzrd_register_clk(struct device *dev, const 
char *name,
        case WZRD_CLK_DIV:
                cwc = &cw->div_data;
                int_reg_field = &clk_wzrd_divclk_divide;
+               rate_constraint = WZRD_RATE_PFD;
                parent_name = __clk_get_name(cw->clk_in1);
                break;
        case WZRD_CLK_PLL:
@@ -513,6 +612,7 @@ static int clk_wzrd_register_clk(struct device *dev, const 
char *name,
                        frac_reg_field = &clk_wzrd_clkfbout_frac;
                        cwc->flags |= WZRD_FLAG_FRAC;
                }
+               rate_constraint = WZRD_RATE_VCO;
                parent_name = clk_hw_get_name(&cw->div_data.hw);
                break;
        case WZRD_CLK_OUT:
@@ -524,10 +624,12 @@ static int clk_wzrd_register_clk(struct device *dev, 
const char *name,
                if (instance == 0) {
                        if (cw->chip->cell % 2 == 0) {
                                cwc->flags |= WZRD_FLAG_FRAC;
+                               cwc->flags |= WZRD_FLAG_ADJUST_MIN;
                                frac_reg_field = &clk_wzrd_clkout0_frac;
                        }
                }
                int_reg_field = &clk_wzrd_clkout_divide[instance];
+               rate_constraint = WZRD_RATE_OUT;
                parent_name = clk_hw_get_name(&cw->pll_data.hw);
                break;
        default:
@@ -539,6 +641,7 @@ static int clk_wzrd_register_clk(struct device *dev, const 
char *name,
        init.parent_names = &parent_name;
        init.num_parents = 1;
        cwc->hw.init = &init;
+       cwc->ratio_limit = &ratio_constraints[cw->chip->cell][type];
        cwc->int_field = devm_regmap_field_alloc(dev, cw->regmap,
                                                 *int_reg_field);
        if (IS_ERR(cwc->int_field)) {
@@ -559,6 +662,10 @@ static int clk_wzrd_register_clk(struct device *dev, const 
char *name,
        if (ret)
                goto err;
 
+       clk_hw_set_rate_range(&cwc->hw,
+                             cw->chip->min[rate_constraint],
+                             cw->chip->max[cw->speed_grade][rate_constraint]);
+
        return 0;
 err:
        dev_err(dev, "Unable to register component clock %s\n", name);
-- 
2.15.1 (Apple Git-101)

_______________________________________________
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

Reply via email to