diff options
author | Peter De Schrijver <pdeschrijver@nvidia.com> | 2017-02-28 16:37:21 +0200 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2017-03-20 14:07:33 +0100 |
commit | e827ba1840bc6a9540deb81c6df6943a19e0e891 (patch) | |
tree | 17c61ab18ac405e8cfb52930787367f285e16f73 /drivers/clk/tegra | |
parent | 6cfc8bc9ee66677fbd1b3331167d6f520e30b6fd (diff) |
clk: tegra: Add super clock mux/divider
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>
Diffstat (limited to 'drivers/clk/tegra')
-rw-r--r-- | drivers/clk/tegra/clk-super.c | 87 | ||||
-rw-r--r-- | drivers/clk/tegra/clk.h | 7 |
2 files changed, 89 insertions, 5 deletions
diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c index 131d1b5085e2..84267cfc4433 100644 --- a/drivers/clk/tegra/clk-super.c +++ b/drivers/clk/tegra/clk-super.c @@ -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; +} diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 8b09021eee00..960e47e41fdf 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -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 |