Provide initial support for CCF clk_set_rate API on all clock components. Clock consumers that want to set the first divider or PLL clock will need to use clk_get_parent on one of the output clocks as there is no support for CLK_SET_RATE_PARENT yet. Care must be taken when setting the first divider clock to ensure that the PLL clock rate will remain within a valid range for the VCO, as it is impossible to subsequently update any clock if the PLL does not lock. A subsequent patch will address this issue. Signed-off-by: James Kelly <jamespeterkelly@xxxxxxxxx> --- drivers/staging/clocking-wizard/TODO | 4 +- .../clocking-wizard/clk-xlnx-clock-wizard.c | 115 +++++++++++++++++++++ 2 files changed, 117 insertions(+), 2 deletions(-) diff --git a/drivers/staging/clocking-wizard/TODO b/drivers/staging/clocking-wizard/TODO index 53c9941fcc35..50193bdd61e1 100644 --- a/drivers/staging/clocking-wizard/TODO +++ b/drivers/staging/clocking-wizard/TODO @@ -1,8 +1,8 @@ TODO: - - 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? + - implement CLK_SET_RATE_PARENT to set internal clocks + - implement CLK_SET_RATE_PARENT to set input clock - test on 64-bit ARM and Microblaze architectures. - support clk_set_phase diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c index 8828dac6faaf..455ee9887c77 100644 --- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c +++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c @@ -76,6 +76,7 @@ #define KHz 1000UL #define MHz 1000000UL #define WZRD_ACLK_MAX_FREQ (250 * MHz) +#define WZRD_PLL_LOCK_TIMEOUT 1000 // usec #define WZRD_FRAC_BITS 3 #define WZRD_FRAC_MASK (BIT(WZRD_FRAC_BITS) - 1) #define WZRD_FRAC_SCALE (1000 >> WZRD_FRAC_BITS) @@ -85,6 +86,8 @@ #define WZRD_FLAG_FRAC BIT(1) #define WZRD_FLAG_ADJUST_MIN BIT(2) +struct clk_wzrd; + /* * Clock rate constraints extracted from Xilinx data sheets listed below. * The minimum rates depend on family and clock type and the maximum rates @@ -310,6 +313,7 @@ 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 + * @cw; pointer to platform device data * @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 @@ -322,6 +326,7 @@ static const struct reg_field clk_wzrd_reconfig = REG_FIELD(0x25C, 0, 1); struct clk_wzrd_clk_data { struct clk_hw hw; unsigned long flags; + struct clk_wzrd *cw; const struct clk_wzrd_ratio *ratio_limit; struct regmap_field *int_field; struct regmap_field *frac_field; @@ -344,6 +349,9 @@ struct clk_wzrd_clk_data { * @div: Divider internal clock provider data * @pll: Phase locked loop internal clock provider data * @chip: Chip data including rate constraints + * @pll_locked: Phase locked loop locked status regmap field + * @reconfig: Reconfiguration regmap field + * @dev: Handle to device * @clkout_data: Array of output clock provider data */ struct clk_wzrd { @@ -358,6 +366,9 @@ struct clk_wzrd { struct clk_wzrd_clk_data div_data; struct clk_wzrd_clk_data pll_data; const struct clk_wzrd_chip *chip; + struct regmap_field *pll_locked; + struct regmap_field *reconfig; + struct device *dev; struct clk_wzrd_clk_data clkout_data[0]; }; #define to_clk_wzrd(_nb) container_of(_nb, struct clk_wzrd, nb) @@ -509,6 +520,97 @@ static int clk_wzrd_determine_rate(struct clk_hw *hw, return 0; } +static bool clk_wzrd_set_ratio(struct clk_wzrd_clk_data *cwc, + unsigned long parent_rate, + unsigned long rate) +{ + unsigned long old_ratio = clk_wzrd_get_ratio(cwc); + unsigned long ratio = clk_wzrd_limit_calc_ratio(cwc, parent_rate, rate); + + if (ratio == old_ratio) + return false; + + regmap_field_write(cwc->int_field, ratio >> WZRD_FRAC_BITS); + if (cwc->flags & WZRD_FLAG_FRAC) + regmap_field_write(cwc->frac_field, (ratio & WZRD_FRAC_MASK) * + WZRD_FRAC_SCALE); + + return true; +} + +/* + * Wait for PLL to become locked + * + * The main reason the PLL will not be locked is because the parent clock + * changed rate. It is entirely possible that the PLL will never lock and + * the only way to fix this is to change the parent clock rate, as dynamic + * reconfiguration of the first divider and PLL multiplier is not possible + * when the PLL is not locked! + * + * Returns true if PLL is already locked or obtains lock within the timeout. + */ +static inline bool clk_wzrd_wait_pll_lock(struct clk_wzrd *cw) +{ + u32 status; + + return !regmap_field_read_poll_timeout(cw->pll_locked, status, + (status == 1), 100, + WZRD_PLL_LOCK_TIMEOUT); +} + +/* + * Wait for dynamic reconfiguration of Clocking Wizard IP to complete + * + * According to the Xilinx documentation the reconfiguration is + * not completed until the PLL is locked so we use the same timeout + * logic as we used for clk_wzrd_wait_pll_lock. + * + * Returns true if reconfiguration succeeded and the PLL locked. + */ +static inline bool clk_wzrd_wait_reconfigure(struct clk_wzrd *cw) +{ + u32 status; + + return !regmap_field_read_poll_timeout(cw->reconfig, status, + ((status & 1) == 0), + 100, WZRD_PLL_LOCK_TIMEOUT); +} + +static inline int clk_wzrd_reconfigure(struct clk_wzrd *cw) +{ + regmap_field_write(cw->reconfig, 3); + + if (!clk_wzrd_wait_reconfigure(cw)) { + dev_err(cw->dev, "Reconfiguration failed\n"); + return -EIO; + } + + return 0; +} + +static int clk_wzrd_set_rate(struct clk_hw *hw, unsigned long req_rate, + unsigned long parent_rate) +{ + struct clk_wzrd_clk_data *cwc = to_clk_wzrd_clk_data(hw); + struct clk_wzrd *cw = cwc->cw; + + if (cwc->flags & WZRD_FLAG_MULTIPLY && parent_rate == 0) + return -EINVAL; + + if (!(cwc->flags & WZRD_FLAG_MULTIPLY) && req_rate == 0) + return -EINVAL; + + if (!clk_wzrd_wait_pll_lock(cw)) { + dev_err(cw->dev, "Set rate not possible - PLL not locked\n"); + return -EIO; + } + + if (!clk_wzrd_set_ratio(cwc, parent_rate, req_rate)) + return 0; + + return clk_wzrd_reconfigure(cw); +} + #ifdef CONFIG_DEBUG_FS static int clk_wzrd_flags_show(struct seq_file *s, void *data) @@ -576,6 +678,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, + .set_rate = clk_wzrd_set_rate, #ifdef CONFIG_DEBUG_FS .debug_init = clk_wzrd_debug_init, #endif @@ -641,6 +744,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->cw = cw; cwc->ratio_limit = &ratio_constraints[cw->chip->cell][type]; cwc->int_field = devm_regmap_field_alloc(dev, cw->regmap, *int_reg_field); @@ -770,6 +874,7 @@ static int clk_wzrd_get_device_tree_data(struct device *dev) if (!cw) return -ENOMEM; dev_set_drvdata(dev, cw); + cw->dev = dev; cw->chip = &chip_constraints[family][type]; cw->clk_data.clk_num = num_outputs; @@ -842,6 +947,16 @@ static int clk_wzrd_regmap_alloc(struct device *dev) if (IS_ERR(cw->regmap)) return PTR_ERR(cw->regmap); + cw->pll_locked = devm_regmap_field_alloc(dev, cw->regmap, + clk_wzrd_status_locked); + if (IS_ERR(cw->pll_locked)) + return PTR_ERR(cw->pll_locked); + + cw->reconfig = devm_regmap_field_alloc(dev, cw->regmap, + clk_wzrd_reconfig); + if (IS_ERR(cw->reconfig)) + return PTR_ERR(cw->reconfig); + return 0; } -- 2.15.1 (Apple Git-101) _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel