123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- /*
- * Copyright (C) 2012 ST Microelectronics
- * Viresh Kumar <vireshk@kernel.org>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- *
- * Auxiliary Synthesizer clock implementation
- */
- #define pr_fmt(fmt) "clk-aux-synth: " fmt
- #include <linux/clk-provider.h>
- #include <linux/slab.h>
- #include <linux/io.h>
- #include <linux/err.h>
- #include "clk.h"
- /*
- * DOC: Auxiliary Synthesizer clock
- *
- * Aux synth gives rate for different values of eq, x and y
- *
- * Fout from synthesizer can be given from two equations:
- * Fout1 = (Fin * X/Y)/2 EQ1
- * Fout2 = Fin * X/Y EQ2
- */
- #define to_clk_aux(_hw) container_of(_hw, struct clk_aux, hw)
- static struct aux_clk_masks default_aux_masks = {
- .eq_sel_mask = AUX_EQ_SEL_MASK,
- .eq_sel_shift = AUX_EQ_SEL_SHIFT,
- .eq1_mask = AUX_EQ1_SEL,
- .eq2_mask = AUX_EQ2_SEL,
- .xscale_sel_mask = AUX_XSCALE_MASK,
- .xscale_sel_shift = AUX_XSCALE_SHIFT,
- .yscale_sel_mask = AUX_YSCALE_MASK,
- .yscale_sel_shift = AUX_YSCALE_SHIFT,
- .enable_bit = AUX_SYNT_ENB,
- };
- static unsigned long aux_calc_rate(struct clk_hw *hw, unsigned long prate,
- int index)
- {
- struct clk_aux *aux = to_clk_aux(hw);
- struct aux_rate_tbl *rtbl = aux->rtbl;
- u8 eq = rtbl[index].eq ? 1 : 2;
- return (((prate / 10000) * rtbl[index].xscale) /
- (rtbl[index].yscale * eq)) * 10000;
- }
- static long clk_aux_round_rate(struct clk_hw *hw, unsigned long drate,
- unsigned long *prate)
- {
- struct clk_aux *aux = to_clk_aux(hw);
- int unused;
- return clk_round_rate_index(hw, drate, *prate, aux_calc_rate,
- aux->rtbl_cnt, &unused);
- }
- static unsigned long clk_aux_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
- {
- struct clk_aux *aux = to_clk_aux(hw);
- unsigned int num = 1, den = 1, val, eqn;
- unsigned long flags = 0;
- if (aux->lock)
- spin_lock_irqsave(aux->lock, flags);
- val = readl_relaxed(aux->reg);
- if (aux->lock)
- spin_unlock_irqrestore(aux->lock, flags);
- eqn = (val >> aux->masks->eq_sel_shift) & aux->masks->eq_sel_mask;
- if (eqn == aux->masks->eq1_mask)
- den = 2;
- /* calculate numerator */
- num = (val >> aux->masks->xscale_sel_shift) &
- aux->masks->xscale_sel_mask;
- /* calculate denominator */
- den *= (val >> aux->masks->yscale_sel_shift) &
- aux->masks->yscale_sel_mask;
- if (!den)
- return 0;
- return (((parent_rate / 10000) * num) / den) * 10000;
- }
- /* Configures new clock rate of aux */
- static int clk_aux_set_rate(struct clk_hw *hw, unsigned long drate,
- unsigned long prate)
- {
- struct clk_aux *aux = to_clk_aux(hw);
- struct aux_rate_tbl *rtbl = aux->rtbl;
- unsigned long val, flags = 0;
- int i;
- clk_round_rate_index(hw, drate, prate, aux_calc_rate, aux->rtbl_cnt,
- &i);
- if (aux->lock)
- spin_lock_irqsave(aux->lock, flags);
- val = readl_relaxed(aux->reg) &
- ~(aux->masks->eq_sel_mask << aux->masks->eq_sel_shift);
- val |= (rtbl[i].eq & aux->masks->eq_sel_mask) <<
- aux->masks->eq_sel_shift;
- val &= ~(aux->masks->xscale_sel_mask << aux->masks->xscale_sel_shift);
- val |= (rtbl[i].xscale & aux->masks->xscale_sel_mask) <<
- aux->masks->xscale_sel_shift;
- val &= ~(aux->masks->yscale_sel_mask << aux->masks->yscale_sel_shift);
- val |= (rtbl[i].yscale & aux->masks->yscale_sel_mask) <<
- aux->masks->yscale_sel_shift;
- writel_relaxed(val, aux->reg);
- if (aux->lock)
- spin_unlock_irqrestore(aux->lock, flags);
- return 0;
- }
- static struct clk_ops clk_aux_ops = {
- .recalc_rate = clk_aux_recalc_rate,
- .round_rate = clk_aux_round_rate,
- .set_rate = clk_aux_set_rate,
- };
- struct clk *clk_register_aux(const char *aux_name, const char *gate_name,
- const char *parent_name, unsigned long flags, void __iomem *reg,
- struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl,
- u8 rtbl_cnt, spinlock_t *lock, struct clk **gate_clk)
- {
- struct clk_aux *aux;
- struct clk_init_data init;
- struct clk *clk;
- if (!aux_name || !parent_name || !reg || !rtbl || !rtbl_cnt) {
- pr_err("Invalid arguments passed");
- return ERR_PTR(-EINVAL);
- }
- aux = kzalloc(sizeof(*aux), GFP_KERNEL);
- if (!aux) {
- pr_err("could not allocate aux clk\n");
- return ERR_PTR(-ENOMEM);
- }
- /* struct clk_aux assignments */
- if (!masks)
- aux->masks = &default_aux_masks;
- else
- aux->masks = masks;
- aux->reg = reg;
- aux->rtbl = rtbl;
- aux->rtbl_cnt = rtbl_cnt;
- aux->lock = lock;
- aux->hw.init = &init;
- init.name = aux_name;
- init.ops = &clk_aux_ops;
- init.flags = flags;
- init.parent_names = &parent_name;
- init.num_parents = 1;
- clk = clk_register(NULL, &aux->hw);
- if (IS_ERR_OR_NULL(clk))
- goto free_aux;
- if (gate_name) {
- struct clk *tgate_clk;
- tgate_clk = clk_register_gate(NULL, gate_name, aux_name,
- CLK_SET_RATE_PARENT, reg,
- aux->masks->enable_bit, 0, lock);
- if (IS_ERR_OR_NULL(tgate_clk))
- goto free_aux;
- if (gate_clk)
- *gate_clk = tgate_clk;
- }
- return clk;
- free_aux:
- kfree(aux);
- pr_err("clk register failed\n");
- return NULL;
- }
|