diff options
Diffstat (limited to 'drivers/clk/mmp/clk-frac.c')
-rw-r--r-- | drivers/clk/mmp/clk-frac.c | 74 |
1 files changed, 58 insertions, 16 deletions
diff --git a/drivers/clk/mmp/clk-frac.c b/drivers/clk/mmp/clk-frac.c index 23a56f561812..584a9927993b 100644 --- a/drivers/clk/mmp/clk-frac.c +++ b/drivers/clk/mmp/clk-frac.c @@ -22,19 +22,12 @@ * numerator/denominator = Fin / (Fout * factor) */ -#define to_clk_factor(hw) container_of(hw, struct clk_factor, hw) -struct clk_factor { - struct clk_hw hw; - void __iomem *base; - struct clk_factor_masks *masks; - struct clk_factor_tbl *ftbl; - unsigned int ftbl_cnt; -}; +#define to_clk_factor(hw) container_of(hw, struct mmp_clk_factor, hw) static long clk_factor_round_rate(struct clk_hw *hw, unsigned long drate, unsigned long *prate) { - struct clk_factor *factor = to_clk_factor(hw); + struct mmp_clk_factor *factor = to_clk_factor(hw); unsigned long rate = 0, prev_rate; int i; @@ -58,8 +51,8 @@ static long clk_factor_round_rate(struct clk_hw *hw, unsigned long drate, static unsigned long clk_factor_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct clk_factor *factor = to_clk_factor(hw); - struct clk_factor_masks *masks = factor->masks; + struct mmp_clk_factor *factor = to_clk_factor(hw); + struct mmp_clk_factor_masks *masks = factor->masks; unsigned int val, num, den; val = readl_relaxed(factor->base); @@ -81,11 +74,12 @@ static unsigned long clk_factor_recalc_rate(struct clk_hw *hw, static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate, unsigned long prate) { - struct clk_factor *factor = to_clk_factor(hw); - struct clk_factor_masks *masks = factor->masks; + struct mmp_clk_factor *factor = to_clk_factor(hw); + struct mmp_clk_factor_masks *masks = factor->masks; int i; unsigned long val; unsigned long prev_rate, rate = 0; + unsigned long flags = 0; for (i = 0; i < factor->ftbl_cnt; i++) { prev_rate = rate; @@ -97,6 +91,9 @@ static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate, if (i > 0) i--; + if (factor->lock) + spin_lock_irqsave(factor->lock, flags); + val = readl_relaxed(factor->base); val &= ~(masks->num_mask << masks->num_shift); @@ -107,21 +104,65 @@ static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate, writel_relaxed(val, factor->base); + if (factor->lock) + spin_unlock_irqrestore(factor->lock, flags); + return 0; } +static void clk_factor_init(struct clk_hw *hw) +{ + struct mmp_clk_factor *factor = to_clk_factor(hw); + struct mmp_clk_factor_masks *masks = factor->masks; + u32 val, num, den; + int i; + unsigned long flags = 0; + + if (factor->lock) + spin_lock_irqsave(factor->lock, flags); + + val = readl(factor->base); + + /* calculate numerator */ + num = (val >> masks->num_shift) & masks->num_mask; + + /* calculate denominator */ + den = (val >> masks->den_shift) & masks->den_mask; + + for (i = 0; i < factor->ftbl_cnt; i++) + if (den == factor->ftbl[i].den && num == factor->ftbl[i].num) + break; + + if (i >= factor->ftbl_cnt) { + val &= ~(masks->num_mask << masks->num_shift); + val |= (factor->ftbl[0].num & masks->num_mask) << + masks->num_shift; + + val &= ~(masks->den_mask << masks->den_shift); + val |= (factor->ftbl[0].den & masks->den_mask) << + masks->den_shift; + + writel(val, factor->base); + } + + if (factor->lock) + spin_unlock_irqrestore(factor->lock, flags); +} + static struct clk_ops clk_factor_ops = { .recalc_rate = clk_factor_recalc_rate, .round_rate = clk_factor_round_rate, .set_rate = clk_factor_set_rate, + .init = clk_factor_init, }; struct clk *mmp_clk_register_factor(const char *name, const char *parent_name, unsigned long flags, void __iomem *base, - struct clk_factor_masks *masks, struct clk_factor_tbl *ftbl, - unsigned int ftbl_cnt) + struct mmp_clk_factor_masks *masks, + struct mmp_clk_factor_tbl *ftbl, + unsigned int ftbl_cnt, spinlock_t *lock) { - struct clk_factor *factor; + struct mmp_clk_factor *factor; struct clk_init_data init; struct clk *clk; @@ -142,6 +183,7 @@ struct clk *mmp_clk_register_factor(const char *name, const char *parent_name, factor->ftbl = ftbl; factor->ftbl_cnt = ftbl_cnt; factor->hw.init = &init; + factor->lock = lock; init.name = name; init.ops = &clk_factor_ops; |