diff options
Diffstat (limited to 'drivers/clk/ingenic/cgu.c')
-rw-r--r-- | drivers/clk/ingenic/cgu.c | 92 |
1 files changed, 60 insertions, 32 deletions
diff --git a/drivers/clk/ingenic/cgu.c b/drivers/clk/ingenic/cgu.c index c8e9cb6c8e39..266c7595d330 100644 --- a/drivers/clk/ingenic/cgu.c +++ b/drivers/clk/ingenic/cgu.c @@ -99,13 +99,14 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) od_enc = ctl >> pll_info->od_shift; od_enc &= GENMASK(pll_info->od_bits - 1, 0); - ctl = readl(cgu->base + pll_info->bypass_reg); + if (pll_info->bypass_bit >= 0) { + ctl = readl(cgu->base + pll_info->bypass_reg); - bypass = !pll_info->no_bypass_bit && - !!(ctl & BIT(pll_info->bypass_bit)); + bypass = !!(ctl & BIT(pll_info->bypass_bit)); - if (bypass) - return parent_rate; + if (bypass) + return parent_rate; + } for (od = 0; od < pll_info->od_max; od++) { if (pll_info->od_encoding[od] == od_enc) @@ -118,28 +119,42 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) n * od); } -static unsigned long -ingenic_pll_calc(const struct ingenic_cgu_clk_info *clk_info, - unsigned long rate, unsigned long parent_rate, - unsigned *pm, unsigned *pn, unsigned *pod) +static void +ingenic_pll_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info, + unsigned long rate, unsigned long parent_rate, + unsigned int *pm, unsigned int *pn, unsigned int *pod) { - const struct ingenic_cgu_pll_info *pll_info; - unsigned m, n, od; - - pll_info = &clk_info->pll; - od = 1; + unsigned int m, n, od = 1; /* * The frequency after the input divider must be between 10 and 50 MHz. * The highest divider yields the best resolution. */ n = parent_rate / (10 * MHZ); - n = min_t(unsigned, n, 1 << clk_info->pll.n_bits); - n = max_t(unsigned, n, pll_info->n_offset); + n = min_t(unsigned int, n, 1 << pll_info->n_bits); + n = max_t(unsigned int, n, pll_info->n_offset); m = (rate / MHZ) * od * n / (parent_rate / MHZ); - m = min_t(unsigned, m, 1 << clk_info->pll.m_bits); - m = max_t(unsigned, m, pll_info->m_offset); + m = min_t(unsigned int, m, 1 << pll_info->m_bits); + m = max_t(unsigned int, m, pll_info->m_offset); + + *pm = m; + *pn = n; + *pod = od; +} + +static unsigned long +ingenic_pll_calc(const struct ingenic_cgu_clk_info *clk_info, + unsigned long rate, unsigned long parent_rate, + unsigned int *pm, unsigned int *pn, unsigned int *pod) +{ + const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; + unsigned int m, n, od; + + if (pll_info->calc_m_n_od) + (*pll_info->calc_m_n_od)(pll_info, rate, parent_rate, &m, &n, &od); + else + ingenic_pll_calc_m_n_od(pll_info, rate, parent_rate, &m, &n, &od); if (pm) *pm = m; @@ -225,11 +240,13 @@ static int ingenic_pll_enable(struct clk_hw *hw) u32 ctl; spin_lock_irqsave(&cgu->lock, flags); - ctl = readl(cgu->base + pll_info->bypass_reg); + if (pll_info->bypass_bit >= 0) { + ctl = readl(cgu->base + pll_info->bypass_reg); - ctl &= ~BIT(pll_info->bypass_bit); + ctl &= ~BIT(pll_info->bypass_bit); - writel(ctl, cgu->base + pll_info->bypass_reg); + writel(ctl, cgu->base + pll_info->bypass_reg); + } ctl = readl(cgu->base + pll_info->reg); @@ -369,18 +386,23 @@ ingenic_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) struct ingenic_cgu *cgu = ingenic_clk->cgu; unsigned long rate = parent_rate; u32 div_reg, div; + u8 parent; if (clk_info->type & CGU_CLK_DIV) { - div_reg = readl(cgu->base + clk_info->div.reg); - div = (div_reg >> clk_info->div.shift) & - GENMASK(clk_info->div.bits - 1, 0); + parent = ingenic_clk_get_parent(hw); - if (clk_info->div.div_table) - div = clk_info->div.div_table[div]; - else - div = (div + 1) * clk_info->div.div; + if (!(clk_info->div.bypass_mask & BIT(parent))) { + div_reg = readl(cgu->base + clk_info->div.reg); + div = (div_reg >> clk_info->div.shift) & + GENMASK(clk_info->div.bits - 1, 0); - rate /= div; + if (clk_info->div.div_table) + div = clk_info->div.div_table[div]; + else + div = (div + 1) * clk_info->div.div; + + rate /= div; + } } else if (clk_info->type & CGU_CLK_FIXDIV) { rate /= clk_info->fixdiv.div; } @@ -410,10 +432,16 @@ ingenic_clk_calc_hw_div(const struct ingenic_cgu_clk_info *clk_info, } static unsigned -ingenic_clk_calc_div(const struct ingenic_cgu_clk_info *clk_info, +ingenic_clk_calc_div(struct clk_hw *hw, + const struct ingenic_cgu_clk_info *clk_info, unsigned long parent_rate, unsigned long req_rate) { unsigned int div, hw_div; + u8 parent; + + parent = ingenic_clk_get_parent(hw); + if (clk_info->div.bypass_mask & BIT(parent)) + return 1; /* calculate the divide */ div = DIV_ROUND_UP(parent_rate, req_rate); @@ -448,7 +476,7 @@ ingenic_clk_round_rate(struct clk_hw *hw, unsigned long req_rate, unsigned int div = 1; if (clk_info->type & CGU_CLK_DIV) - div = ingenic_clk_calc_div(clk_info, *parent_rate, req_rate); + div = ingenic_clk_calc_div(hw, clk_info, *parent_rate, req_rate); else if (clk_info->type & CGU_CLK_FIXDIV) div = clk_info->fixdiv.div; else if (clk_hw_can_set_rate_parent(hw)) @@ -480,7 +508,7 @@ ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate, int ret = 0; if (clk_info->type & CGU_CLK_DIV) { - div = ingenic_clk_calc_div(clk_info, parent_rate, req_rate); + div = ingenic_clk_calc_div(hw, clk_info, parent_rate, req_rate); rate = DIV_ROUND_UP(parent_rate, div); if (rate != req_rate) |