Skip to content

Commit

Permalink
Merge tag 'qcom-clocks-for-3.18' of git://git.kernel.org/pub/scm/linu…
Browse files Browse the repository at this point in the history
…x/kernel/git/galak/linux-qcom into clk-next

qcom clock changes for 3.18

Some fixes for the IPQ driver and some code consolidation
and refactoring.
  • Loading branch information
Mike Turquette committed Sep 26, 2014
2 parents b6b2fe5 + 229fd4a commit db0bcc3
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 88 deletions.
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
115 changes: 54 additions & 61 deletions drivers/clk/qcom/clk-rcg.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <asm/div64.h>

#include "clk-rcg.h"
#include "common.h"

static u32 ns_to_src(struct src_sel *s, u32 ns)
{
Expand Down Expand Up @@ -67,16 +68,16 @@ static u8 clk_dyn_rcg_get_parent(struct clk_hw *hw)
{
struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
int num_parents = __clk_get_num_parents(hw->clk);
u32 ns, ctl;
u32 ns, reg;
int bank;
int i;
struct src_sel *s;

regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl);
bank = reg_to_bank(rcg, ctl);
regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
bank = reg_to_bank(rcg, reg);
s = &rcg->s[bank];

regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns);
ns = ns_to_src(s, ns);

for (i = 0; i < num_parents; i++)
Expand Down Expand Up @@ -192,90 +193,93 @@ static u32 mn_to_reg(struct mn *mn, u32 m, u32 n, u32 val)

static void configure_bank(struct clk_dyn_rcg *rcg, const struct freq_tbl *f)
{
u32 ns, md, ctl, *regp;
u32 ns, md, reg;
int bank, new_bank;
struct mn *mn;
struct pre_div *p;
struct src_sel *s;
bool enabled;
u32 md_reg;
u32 bank_reg;
u32 md_reg, ns_reg;
bool banked_mn = !!rcg->mn[1].width;
bool banked_p = !!rcg->p[1].pre_div_width;
struct clk_hw *hw = &rcg->clkr.hw;

enabled = __clk_is_enabled(hw->clk);

regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl);

if (banked_mn) {
regp = &ctl;
bank_reg = rcg->clkr.enable_reg;
} else {
regp = &ns;
bank_reg = rcg->ns_reg;
}

bank = reg_to_bank(rcg, *regp);
regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
bank = reg_to_bank(rcg, reg);
new_bank = enabled ? !bank : bank;

ns_reg = rcg->ns_reg[new_bank];
regmap_read(rcg->clkr.regmap, ns_reg, &ns);

if (banked_mn) {
mn = &rcg->mn[new_bank];
md_reg = rcg->md_reg[new_bank];

ns |= BIT(mn->mnctr_reset_bit);
regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
regmap_write(rcg->clkr.regmap, ns_reg, ns);

regmap_read(rcg->clkr.regmap, md_reg, &md);
md = mn_to_md(mn, f->m, f->n, md);
regmap_write(rcg->clkr.regmap, md_reg, md);

ns = mn_to_ns(mn, f->m, f->n, ns);
regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
regmap_write(rcg->clkr.regmap, ns_reg, ns);

ctl = mn_to_reg(mn, f->m, f->n, ctl);
regmap_write(rcg->clkr.regmap, rcg->clkr.enable_reg, ctl);
/* Two NS registers means mode control is in NS register */
if (rcg->ns_reg[0] != rcg->ns_reg[1]) {
ns = mn_to_reg(mn, f->m, f->n, ns);
regmap_write(rcg->clkr.regmap, ns_reg, ns);
} else {
reg = mn_to_reg(mn, f->m, f->n, reg);
regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg);
}

ns &= ~BIT(mn->mnctr_reset_bit);
regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
} else {
regmap_write(rcg->clkr.regmap, ns_reg, ns);
}

if (banked_p) {
p = &rcg->p[new_bank];
ns = pre_div_to_ns(p, f->pre_div - 1, ns);
}

s = &rcg->s[new_bank];
ns = src_to_ns(s, s->parent_map[f->src], ns);
regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
regmap_write(rcg->clkr.regmap, ns_reg, ns);

if (enabled) {
*regp ^= BIT(rcg->mux_sel_bit);
regmap_write(rcg->clkr.regmap, bank_reg, *regp);
regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
reg ^= BIT(rcg->mux_sel_bit);
regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg);
}
}

static int clk_dyn_rcg_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
u32 ns, ctl, md, reg;
u32 ns, md, reg;
int bank;
struct freq_tbl f = { 0 };
bool banked_mn = !!rcg->mn[1].width;
bool banked_p = !!rcg->p[1].pre_div_width;

regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl);
reg = banked_mn ? ctl : ns;

regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
bank = reg_to_bank(rcg, reg);

regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns);

if (banked_mn) {
regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md);
f.m = md_to_m(&rcg->mn[bank], md);
f.n = ns_m_to_n(&rcg->mn[bank], ns, f.m);
} else {
f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1;
}
f.src = index;

if (banked_p)
f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1;

f.src = index;
configure_bank(rcg, &f);

return 0;
Expand Down Expand Up @@ -336,41 +340,30 @@ clk_dyn_rcg_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
u32 m, n, pre_div, ns, md, mode, reg;
int bank;
struct mn *mn;
bool banked_p = !!rcg->p[1].pre_div_width;
bool banked_mn = !!rcg->mn[1].width;

regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);

if (banked_mn)
regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &reg);
else
reg = ns;

regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
bank = reg_to_bank(rcg, reg);

regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns);
m = n = pre_div = mode = 0;

if (banked_mn) {
mn = &rcg->mn[bank];
regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md);
m = md_to_m(mn, md);
n = ns_m_to_n(mn, ns, m);
/* Two NS registers means mode control is in NS register */
if (rcg->ns_reg[0] != rcg->ns_reg[1])
reg = ns;
mode = reg_to_mnctr_mode(mn, reg);
return calc_rate(parent_rate, m, n, mode, 0);
} else {
pre_div = ns_to_pre_div(&rcg->p[bank], ns);
return calc_rate(parent_rate, 0, 0, 0, pre_div);
}
}

static const
struct freq_tbl *find_freq(const struct freq_tbl *f, unsigned long rate)
{
if (!f)
return NULL;

for (; f->freq; f++)
if (rate <= f->freq)
return f;
if (banked_p)
pre_div = ns_to_pre_div(&rcg->p[bank], ns);

return NULL;
return calc_rate(parent_rate, m, n, mode, pre_div);
}

static long _freq_tbl_determine_rate(struct clk_hw *hw,
Expand All @@ -379,7 +372,7 @@ static long _freq_tbl_determine_rate(struct clk_hw *hw,
{
unsigned long clk_flags;

f = find_freq(f, rate);
f = qcom_find_freq(f, rate);
if (!f)
return -EINVAL;

Expand Down Expand Up @@ -477,7 +470,7 @@ static int clk_rcg_set_rate(struct clk_hw *hw, unsigned long rate,
struct clk_rcg *rcg = to_clk_rcg(hw);
const struct freq_tbl *f;

f = find_freq(rcg->freq_tbl, rate);
f = qcom_find_freq(rcg->freq_tbl, rate);
if (!f)
return -EINVAL;

Expand All @@ -497,7 +490,7 @@ static int __clk_dyn_rcg_set_rate(struct clk_hw *hw, unsigned long rate)
struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
const struct freq_tbl *f;

f = find_freq(rcg->freq_tbl, rate);
f = qcom_find_freq(rcg->freq_tbl, rate);
if (!f)
return -EINVAL;

Expand Down
6 changes: 4 additions & 2 deletions drivers/clk/qcom/clk-rcg.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,9 @@ extern const struct clk_ops clk_rcg_bypass_ops;
* struct clk_dyn_rcg - root clock generator with glitch free mux
*
* @mux_sel_bit: bit to switch glitch free mux
* @ns_reg: NS register
* @ns_reg: NS0 and NS1 register
* @md_reg: MD0 and MD1 register
* @bank_reg: register to XOR @mux_sel_bit into to switch glitch free mux
* @mn: mn counter (banked)
* @s: source selector (banked)
* @freq_tbl: frequency table
Expand All @@ -113,8 +114,9 @@ extern const struct clk_ops clk_rcg_bypass_ops;
*
*/
struct clk_dyn_rcg {
u32 ns_reg;
u32 ns_reg[2];
u32 md_reg[2];
u32 bank_reg;

u8 mux_sel_bit;

Expand Down
Loading

0 comments on commit db0bcc3

Please sign in to comment.