[PATCH 08/14] staging: clocking-wizard: Support fractional ratios

James Kelly jamespeterkelly at gmail.com
Mon May 7 01:20:34 UTC 2018


Update clock provider to support fraction divider and multiplier ratios.
Ratios are now fixed point unsigned numbers with the lower WZRD_FRAC_BITS
containing the fractional part of the ratio.

Fractional ratios are only supported on MMCM primitives and only for the
PLL multiplier and first output divider.

Use DIV_ROUND_CLOSEST_ULL for division to provide best precision.

Signed-off-by: James Kelly <jamespeterkelly at gmail.com>
---
 drivers/staging/clocking-wizard/TODO               |  5 +-
 .../clocking-wizard/clk-xlnx-clock-wizard.c        | 68 ++++++++++++++++++++--
 2 files changed, 66 insertions(+), 7 deletions(-)

diff --git a/drivers/staging/clocking-wizard/TODO b/drivers/staging/clocking-wizard/TODO
index ebe99db7d153..53c9941fcc35 100644
--- a/drivers/staging/clocking-wizard/TODO
+++ b/drivers/staging/clocking-wizard/TODO
@@ -1,11 +1,10 @@
 TODO:
-	- support for fractional multiplier
-	- support for fractional divider (output 0 only)
 	- support for set_rate() operations (may benefit from Stephen Boyd's
 	  refactoring of the clk primitives: https://lkml.org/lkml/2014/9/5/766)
 	- review arithmetic
 	  - overflow after multiplication?
-	  - maximize accuracy before divisions
+	- test on 64-bit ARM and Microblaze architectures.
+	- support clk_set_phase
 
 Patches to:
 	Greg Kroah-Hartman <gregkh at linuxfoundation.org>
diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c
index 3e670cdc072c..c892c0d46801 100644
--- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c
+++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c
@@ -74,9 +74,13 @@
 #define KHz			1000UL
 #define MHz			1000000UL
 #define WZRD_ACLK_MAX_FREQ	(250 * MHz)
+#define WZRD_FRAC_BITS		3
+#define WZRD_FRAC_MASK		(BIT(WZRD_FRAC_BITS) - 1)
+#define WZRD_FRAC_SCALE		(1000 >> WZRD_FRAC_BITS)
 #define WZRD_CLKNAME_AXI	"s_axi_aclk"
 #define WZRD_CLKNAME_IN1	"clk_in1"
 #define WZRD_FLAG_MULTIPLY	BIT(0)
+#define WZRD_FLAG_FRAC		BIT(1)
 
 /*
  * Clock rate constraints extracted from Xilinx data sheets listed below.
@@ -304,14 +308,17 @@ 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
  * @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
  */
 struct clk_wzrd_clk_data {
 	struct clk_hw			hw;
 	unsigned long			flags;
 	struct regmap_field		*int_field;
+	struct regmap_field		*frac_field;
 };
 
 #define to_clk_wzrd_clk_data(_hw) \
@@ -349,23 +356,56 @@ struct clk_wzrd {
 };
 #define to_clk_wzrd(_nb) container_of(_nb, struct clk_wzrd, nb)
 
+/*
+ * The following functions use or generate fixed point ratios
+ *
+ * The lower WZRD_FRAC_BITS of fixed point ratios are the fractional
+ * part and the remaining bits are the integer part.
+ *
+ * When doing division we make sure to round once only and at
+ * ether the decimal point or the end of the fixed point number
+ * depending on whether the hardware can multiply/divide by only
+ * integer ratios, or can multiply/divide by fixed point ratios.
+ */
+
 static unsigned long clk_wzrd_get_ratio(struct clk_wzrd_clk_data *cwc)
 {
 	u32 int_part;
+	unsigned long ratio;
 
 	regmap_field_read(cwc->int_field, &int_part);
+	ratio = int_part << WZRD_FRAC_BITS;
+
+	if (cwc->flags & WZRD_FLAG_FRAC) {
+		u32 frac_part;
 
-	return int_part;
+		regmap_field_read(cwc->frac_field, &frac_part);
+		ratio |= frac_part / WZRD_FRAC_SCALE;
+	}
+
+	return ratio;
 }
 
 static unsigned long clk_wzrd_calc_rate(unsigned long parent_rate,
 					unsigned long ratio,
 					unsigned long flags)
 {
-	if (flags & WZRD_FLAG_MULTIPLY)
-		return parent_rate * ratio;
+	unsigned long long t;
 
-	return DIV_ROUND_CLOSEST(parent_rate, ratio);
+	if (flags & WZRD_FLAG_MULTIPLY) {
+		t = (unsigned long long)parent_rate * ratio;
+		return (unsigned long)DIV_ROUND_CLOSEST_ULL(t, BIT(WZRD_FRAC_BITS));
+	}
+
+	if (flags & WZRD_FLAG_FRAC) {
+		/* Round at least significant bit */
+		t = (unsigned long long)parent_rate << WZRD_FRAC_BITS;
+		return (unsigned long)DIV_ROUND_CLOSEST_ULL(t, ratio);
+	}
+
+	/* Round at decimal point */
+	t = (unsigned long long)parent_rate;
+	return (unsigned long)DIV_ROUND_CLOSEST_ULL(t, ratio >> WZRD_FRAC_BITS);
 }
 
 static unsigned long clk_wzrd_recalc_rate(struct clk_hw *hw,
@@ -394,6 +434,7 @@ static int clk_wzrd_register_clk(struct device *dev, const char *name,
 	int ret;
 	struct clk_init_data init;
 	const struct reg_field *int_reg_field;
+	const struct reg_field *frac_reg_field;
 	struct clk_wzrd_clk_data *cwc;
 	const char *parent_name;
 	struct clk_wzrd *cw = dev_get_drvdata(dev);
@@ -411,6 +452,10 @@ static int clk_wzrd_register_clk(struct device *dev, const char *name,
 		cwc = &cw->pll_data;
 		cwc->flags |= WZRD_FLAG_MULTIPLY;
 		int_reg_field = &clk_wzrd_clkfbout_mult;
+		if (cw->chip->cell % 2 == 0) {
+			frac_reg_field = &clk_wzrd_clkfbout_frac;
+			cwc->flags |= WZRD_FLAG_FRAC;
+		}
 		parent_name = clk_hw_get_name(&cw->div_data.hw);
 		break;
 	case WZRD_CLK_OUT:
@@ -419,6 +464,12 @@ static int clk_wzrd_register_clk(struct device *dev, const char *name,
 			goto err;
 		}
 		cwc = &cw->clkout_data[instance];
+		if (instance == 0) {
+			if (cw->chip->cell % 2 == 0) {
+				cwc->flags |= WZRD_FLAG_FRAC;
+				frac_reg_field = &clk_wzrd_clkout0_frac;
+			}
+		}
 		int_reg_field = &clk_wzrd_clkout_divide[instance];
 		parent_name = clk_hw_get_name(&cw->pll_data.hw);
 		break;
@@ -438,6 +489,15 @@ static int clk_wzrd_register_clk(struct device *dev, const char *name,
 		goto err;
 	}
 
+	if (cwc->flags & WZRD_FLAG_FRAC) {
+		cwc->frac_field = devm_regmap_field_alloc(dev, cw->regmap,
+							  *frac_reg_field);
+		if (IS_ERR(cwc->frac_field)) {
+			ret = PTR_ERR(cwc->frac_field);
+			goto err;
+		}
+	}
+
 	ret = devm_clk_hw_register(dev, &cwc->hw);
 	if (ret)
 		goto err;
-- 
2.15.1 (Apple Git-101)



More information about the devel mailing list