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@xxxxxxxxx> --- 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@xxxxxxxxxxxxxxxxxxx> 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) _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel