diff options
author | Paul Cercueil <paul@crapouillou.net> | 2018-01-16 16:47:54 +0100 |
---|---|---|
committer | James Hogan <jhogan@kernel.org> | 2018-01-18 22:05:29 +0000 |
commit | ab27eb4bc3650f34bbf0b371047ce3bfd19aebe9 (patch) | |
tree | f81e6e551eb9ccc3feaff316f23772a627e2dc98 /drivers/clk | |
parent | 268db077ac47d3b5d8e3a768bf9dc5cb32ce6074 (diff) | |
download | talos-obmc-linux-ab27eb4bc3650f34bbf0b371047ce3bfd19aebe9.tar.gz talos-obmc-linux-ab27eb4bc3650f34bbf0b371047ce3bfd19aebe9.zip |
clk: ingenic: Add code to enable/disable PLLs
This commit permits the PLLs to be dynamically enabled and disabled when
their children clocks are enabled and disabled.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Acked-by: Stephen Boyd <sboyd@codeaurora.org>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Maarten ter Huurne <maarten@treewalker.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/18480/
Signed-off-by: James Hogan <jhogan@kernel.org>
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/ingenic/cgu.c | 89 |
1 files changed, 74 insertions, 15 deletions
diff --git a/drivers/clk/ingenic/cgu.c b/drivers/clk/ingenic/cgu.c index 381c4a17a1fc..56a712c9075f 100644 --- a/drivers/clk/ingenic/cgu.c +++ b/drivers/clk/ingenic/cgu.c @@ -107,9 +107,6 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) if (bypass) return parent_rate; - if (!enable) - return 0; - for (od = 0; od < pll_info->od_max; od++) { if (pll_info->od_encoding[od] == od_enc) break; @@ -153,17 +150,25 @@ ingenic_pll_calc(const struct ingenic_cgu_clk_info *clk_info, return div_u64((u64)parent_rate * m, n * od); } -static long -ingenic_pll_round_rate(struct clk_hw *hw, unsigned long req_rate, - unsigned long *prate) +static inline const struct ingenic_cgu_clk_info *to_clk_info( + struct ingenic_clk *ingenic_clk) { - struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); struct ingenic_cgu *cgu = ingenic_clk->cgu; const struct ingenic_cgu_clk_info *clk_info; clk_info = &cgu->clock_info[ingenic_clk->idx]; BUG_ON(clk_info->type != CGU_CLK_PLL); + return clk_info; +} + +static long +ingenic_pll_round_rate(struct clk_hw *hw, unsigned long req_rate, + unsigned long *prate) +{ + struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); + return ingenic_pll_calc(clk_info, req_rate, *prate, NULL, NULL, NULL); } @@ -171,19 +176,14 @@ static int ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate, unsigned long parent_rate) { - const unsigned timeout = 100; struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); struct ingenic_cgu *cgu = ingenic_clk->cgu; - const struct ingenic_cgu_clk_info *clk_info; - const struct ingenic_cgu_pll_info *pll_info; + const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); + const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; unsigned long rate, flags; - unsigned m, n, od, i; + unsigned int m, n, od; u32 ctl; - clk_info = &cgu->clock_info[ingenic_clk->idx]; - BUG_ON(clk_info->type != CGU_CLK_PLL); - pll_info = &clk_info->pll; - rate = ingenic_pll_calc(clk_info, req_rate, parent_rate, &m, &n, &od); if (rate != req_rate) @@ -202,6 +202,26 @@ ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate, ctl &= ~(GENMASK(pll_info->od_bits - 1, 0) << pll_info->od_shift); ctl |= pll_info->od_encoding[od - 1] << pll_info->od_shift; + writel(ctl, cgu->base + pll_info->reg); + spin_unlock_irqrestore(&cgu->lock, flags); + + return 0; +} + +static int ingenic_pll_enable(struct clk_hw *hw) +{ + struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + struct ingenic_cgu *cgu = ingenic_clk->cgu; + const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); + const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; + const unsigned int timeout = 100; + unsigned long flags; + unsigned int i; + u32 ctl; + + spin_lock_irqsave(&cgu->lock, flags); + ctl = readl(cgu->base + pll_info->reg); + ctl &= ~BIT(pll_info->bypass_bit); ctl |= BIT(pll_info->enable_bit); @@ -223,10 +243,48 @@ ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate, return 0; } +static void ingenic_pll_disable(struct clk_hw *hw) +{ + struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + struct ingenic_cgu *cgu = ingenic_clk->cgu; + const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); + const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; + unsigned long flags; + u32 ctl; + + spin_lock_irqsave(&cgu->lock, flags); + ctl = readl(cgu->base + pll_info->reg); + + ctl &= ~BIT(pll_info->enable_bit); + + writel(ctl, cgu->base + pll_info->reg); + spin_unlock_irqrestore(&cgu->lock, flags); +} + +static int ingenic_pll_is_enabled(struct clk_hw *hw) +{ + struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + struct ingenic_cgu *cgu = ingenic_clk->cgu; + const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); + const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; + unsigned long flags; + u32 ctl; + + spin_lock_irqsave(&cgu->lock, flags); + ctl = readl(cgu->base + pll_info->reg); + spin_unlock_irqrestore(&cgu->lock, flags); + + return !!(ctl & BIT(pll_info->enable_bit)); +} + static const struct clk_ops ingenic_pll_ops = { .recalc_rate = ingenic_pll_recalc_rate, .round_rate = ingenic_pll_round_rate, .set_rate = ingenic_pll_set_rate, + + .enable = ingenic_pll_enable, + .disable = ingenic_pll_disable, + .is_enabled = ingenic_pll_is_enabled, }; /* @@ -601,6 +659,7 @@ static int ingenic_register_clock(struct ingenic_cgu *cgu, unsigned idx) } } else if (caps & CGU_CLK_PLL) { clk_init.ops = &ingenic_pll_ops; + clk_init.flags |= CLK_SET_RATE_GATE; caps &= ~CGU_CLK_PLL; |