Add support for the clk_round_rate API to our CCF clock provider. Signed-off-by: James Kelly <jamespeterkelly@xxxxxxxxx> --- .../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 devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel