clk: tegra: Add super clock mux/divider
authorPeter De Schrijver <pdeschrijver@nvidia.com>
Tue, 28 Feb 2017 14:37:21 +0000 (16:37 +0200)
committerThierry Reding <treding@nvidia.com>
Mon, 20 Mar 2017 13:07:33 +0000 (14:07 +0100)
Add a super clock type which implements both mux and divider. This is
used for aclk.

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Reviewed-by: Mikko Perttunen <mperttunen@nvidia.com>
Tested-by: Mikko Perttunen <mperttunen@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
drivers/clk/tegra/clk-super.c
drivers/clk/tegra/clk.h

index 131d1b5..84267cf 100644 (file)
@@ -121,9 +121,50 @@ out:
        return err;
 }
 
+const struct clk_ops tegra_clk_super_mux_ops = {
+       .get_parent = clk_super_get_parent,
+       .set_parent = clk_super_set_parent,
+};
+
+static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate,
+                                unsigned long *parent_rate)
+{
+       struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
+       struct clk_hw *div_hw = &super->frac_div.hw;
+
+       __clk_hw_set_clk(div_hw, hw);
+
+       return super->div_ops->round_rate(div_hw, rate, parent_rate);
+}
+
+static unsigned long clk_super_recalc_rate(struct clk_hw *hw,
+                                          unsigned long parent_rate)
+{
+       struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
+       struct clk_hw *div_hw = &super->frac_div.hw;
+
+       __clk_hw_set_clk(div_hw, hw);
+
+       return super->div_ops->recalc_rate(div_hw, parent_rate);
+}
+
+static int clk_super_set_rate(struct clk_hw *hw, unsigned long rate,
+                             unsigned long parent_rate)
+{
+       struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
+       struct clk_hw *div_hw = &super->frac_div.hw;
+
+       __clk_hw_set_clk(div_hw, hw);
+
+       return super->div_ops->set_rate(div_hw, rate, parent_rate);
+}
+
 const struct clk_ops tegra_clk_super_ops = {
        .get_parent = clk_super_get_parent,
        .set_parent = clk_super_set_parent,
+       .set_rate = clk_super_set_rate,
+       .round_rate = clk_super_round_rate,
+       .recalc_rate = clk_super_recalc_rate,
 };
 
 struct clk *tegra_clk_register_super_mux(const char *name,
@@ -136,13 +177,11 @@ struct clk *tegra_clk_register_super_mux(const char *name,
        struct clk_init_data init;
 
        super = kzalloc(sizeof(*super), GFP_KERNEL);
-       if (!super) {
-               pr_err("%s: could not allocate super clk\n", __func__);
+       if (!super)
                return ERR_PTR(-ENOMEM);
-       }
 
        init.name = name;
-       init.ops = &tegra_clk_super_ops;
+       init.ops = &tegra_clk_super_mux_ops;
        init.flags = flags;
        init.parent_names = parent_names;
        init.num_parents = num_parents;
@@ -163,3 +202,43 @@ struct clk *tegra_clk_register_super_mux(const char *name,
 
        return clk;
 }
+
+struct clk *tegra_clk_register_super_clk(const char *name,
+               const char * const *parent_names, u8 num_parents,
+               unsigned long flags, void __iomem *reg, u8 clk_super_flags,
+               spinlock_t *lock)
+{
+       struct tegra_clk_super_mux *super;
+       struct clk *clk;
+       struct clk_init_data init;
+
+       super = kzalloc(sizeof(*super), GFP_KERNEL);
+       if (!super)
+               return ERR_PTR(-ENOMEM);
+
+       init.name = name;
+       init.ops = &tegra_clk_super_ops;
+       init.flags = flags;
+       init.parent_names = parent_names;
+       init.num_parents = num_parents;
+
+       super->reg = reg;
+       super->lock = lock;
+       super->width = 4;
+       super->flags = clk_super_flags;
+       super->frac_div.reg = reg + 4;
+       super->frac_div.shift = 16;
+       super->frac_div.width = 8;
+       super->frac_div.frac_width = 1;
+       super->frac_div.lock = lock;
+       super->div_ops = &tegra_clk_frac_div_ops;
+
+       /* Data in .init is copied by clk_register(), so stack variable OK */
+       super->hw.init = &init;
+
+       clk = clk_register(NULL, &super->hw);
+       if (IS_ERR(clk))
+               kfree(super);
+
+       return clk;
+}
index 8b09021..960e47e 100644 (file)
@@ -686,6 +686,8 @@ struct tegra_periph_init_data {
 struct tegra_clk_super_mux {
        struct clk_hw   hw;
        void __iomem    *reg;
+       struct tegra_clk_frac_div frac_div;
+       const struct clk_ops    *div_ops;
        u8              width;
        u8              flags;
        u8              div2_index;
@@ -702,7 +704,10 @@ struct clk *tegra_clk_register_super_mux(const char *name,
                const char **parent_names, u8 num_parents,
                unsigned long flags, void __iomem *reg, u8 clk_super_flags,
                u8 width, u8 pllx_index, u8 div2_index, spinlock_t *lock);
-
+struct clk *tegra_clk_register_super_clk(const char *name,
+               const char * const *parent_names, u8 num_parents,
+               unsigned long flags, void __iomem *reg, u8 clk_super_flags,
+               spinlock_t *lock);
 /**
  * struct clk_init_table - clock initialization table
  * @clk_id:    clock id as mentioned in device tree bindings