[PATCH 10/14] staging: clocking-wizard: Support clk_round_rate

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux