[PATCH 10/14] staging: clocking-wizard: Support clk_round_rate
James Kelly
jamespeterkelly at gmail.com
Mon May 7 01:20:36 UTC 2018
Add support for the clk_round_rate API to our CCF clock provider.
Signed-off-by: James Kelly <jamespeterkelly at 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)
More information about the devel
mailing list