Commit ae3669ac authored by Stephen Boyd's avatar Stephen Boyd

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: default avatarStephen Boyd <sboyd@codeaurora.org>
parent 50c6a503
......@@ -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;
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment