clk: qcom: Add support for setting rates on PLLs
Some PLLs may require changing their rate at runtime. Add support for these PLLs. Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
This commit is contained in:
Родитель
50c6a50344
Коммит
ae3669ac5c
|
@ -97,7 +97,7 @@ static unsigned long
|
|||
clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
||||
{
|
||||
struct clk_pll *pll = to_clk_pll(hw);
|
||||
u32 l, m, n;
|
||||
u32 l, m, n, config;
|
||||
unsigned long rate;
|
||||
u64 tmp;
|
||||
|
||||
|
@ -116,13 +116,79 @@ clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
|||
do_div(tmp, n);
|
||||
rate += tmp;
|
||||
}
|
||||
if (pll->post_div_width) {
|
||||
regmap_read(pll->clkr.regmap, pll->config_reg, &config);
|
||||
config >>= pll->post_div_shift;
|
||||
config &= BIT(pll->post_div_width) - 1;
|
||||
rate /= config + 1;
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static const
|
||||
struct pll_freq_tbl *find_freq(const struct pll_freq_tbl *f, unsigned long rate)
|
||||
{
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
for (; f->freq; f++)
|
||||
if (rate <= f->freq)
|
||||
return f;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static long
|
||||
clk_pll_determine_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *p_rate, struct clk **p)
|
||||
{
|
||||
struct clk_pll *pll = to_clk_pll(hw);
|
||||
const struct pll_freq_tbl *f;
|
||||
|
||||
f = find_freq(pll->freq_tbl, rate);
|
||||
if (!f)
|
||||
return clk_pll_recalc_rate(hw, *p_rate);
|
||||
|
||||
return f->freq;
|
||||
}
|
||||
|
||||
static int
|
||||
clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long p_rate)
|
||||
{
|
||||
struct clk_pll *pll = to_clk_pll(hw);
|
||||
const struct pll_freq_tbl *f;
|
||||
bool enabled;
|
||||
u32 mode;
|
||||
u32 enable_mask = PLL_OUTCTRL | PLL_BYPASSNL | PLL_RESET_N;
|
||||
|
||||
f = find_freq(pll->freq_tbl, rate);
|
||||
if (!f)
|
||||
return -EINVAL;
|
||||
|
||||
regmap_read(pll->clkr.regmap, pll->mode_reg, &mode);
|
||||
enabled = (mode & enable_mask) == enable_mask;
|
||||
|
||||
if (enabled)
|
||||
clk_pll_disable(hw);
|
||||
|
||||
regmap_update_bits(pll->clkr.regmap, pll->l_reg, 0x3ff, f->l);
|
||||
regmap_update_bits(pll->clkr.regmap, pll->m_reg, 0x7ffff, f->m);
|
||||
regmap_update_bits(pll->clkr.regmap, pll->n_reg, 0x7ffff, f->n);
|
||||
regmap_write(pll->clkr.regmap, pll->config_reg, f->ibits);
|
||||
|
||||
if (enabled)
|
||||
clk_pll_enable(hw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct clk_ops clk_pll_ops = {
|
||||
.enable = clk_pll_enable,
|
||||
.disable = clk_pll_disable,
|
||||
.recalc_rate = clk_pll_recalc_rate,
|
||||
.determine_rate = clk_pll_determine_rate,
|
||||
.set_rate = clk_pll_set_rate,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_pll_ops);
|
||||
|
||||
|
|
|
@ -17,6 +17,21 @@
|
|||
#include <linux/clk-provider.h>
|
||||
#include "clk-regmap.h"
|
||||
|
||||
/**
|
||||
* struct pll_freq_tbl - PLL frequency table
|
||||
* @l: L value
|
||||
* @m: M value
|
||||
* @n: N value
|
||||
* @ibits: internal values
|
||||
*/
|
||||
struct pll_freq_tbl {
|
||||
unsigned long freq;
|
||||
u16 l;
|
||||
u16 m;
|
||||
u16 n;
|
||||
u32 ibits;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct clk_pll - phase locked loop (PLL)
|
||||
* @l_reg: L register
|
||||
|
@ -26,6 +41,7 @@
|
|||
* @mode_reg: mode register
|
||||
* @status_reg: status register
|
||||
* @status_bit: ANDed with @status_reg to determine if PLL is enabled
|
||||
* @freq_tbl: PLL frequency table
|
||||
* @hw: handle between common and hardware-specific interfaces
|
||||
*/
|
||||
struct clk_pll {
|
||||
|
@ -36,6 +52,10 @@ struct clk_pll {
|
|||
u32 mode_reg;
|
||||
u32 status_reg;
|
||||
u8 status_bit;
|
||||
u8 post_div_width;
|
||||
u8 post_div_shift;
|
||||
|
||||
const struct pll_freq_tbl *freq_tbl;
|
||||
|
||||
struct clk_regmap clkr;
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче