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 <jamespeterke...@gmail.com>
---
 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
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

Reply via email to