Skip to content

Commit

Permalink
clk: qcom: Add support for setting rates on PLLs
Browse files Browse the repository at this point in the history
Some PLLs may require changing their rate at runtime. Add support
for these PLLs.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
  • Loading branch information
Stephen Boyd committed Sep 22, 2014
1 parent 50c6a50 commit ae3669a
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 1 deletion.
68 changes: 67 additions & 1 deletion drivers/clk/qcom/clk-pll.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);

Expand Down
20 changes: 20 additions & 0 deletions drivers/clk/qcom/clk-pll.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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;
};
Expand Down

0 comments on commit ae3669a

Please sign in to comment.