diff options
Diffstat (limited to 'drivers/clk/at91/clk-pll.c')
| -rw-r--r-- | drivers/clk/at91/clk-pll.c | 58 |
1 files changed, 47 insertions, 11 deletions
diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c index b4138fcacf49..5c5f7398effe 100644 --- a/drivers/clk/at91/clk-pll.c +++ b/drivers/clk/at91/clk-pll.c @@ -1,11 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * */ #include <linux/clk-provider.h> @@ -45,6 +40,7 @@ struct clk_pll { u16 mul; const struct clk_pll_layout *layout; const struct clk_pll_characteristics *characteristics; + struct at91_clk_pms pms; }; static inline bool clk_pll_ready(struct regmap *regmap, int id) @@ -235,13 +231,15 @@ static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate, return bestrate; } -static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int clk_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct clk_pll *pll = to_clk_pll(hw); - return clk_pll_get_best_div_mul(pll, rate, *parent_rate, - NULL, NULL, NULL); + req->rate = clk_pll_get_best_div_mul(pll, req->rate, req->best_parent_rate, + NULL, NULL, NULL); + + return 0; } static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, @@ -265,13 +263,51 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } +static int clk_pll_save_context(struct clk_hw *hw) +{ + struct clk_pll *pll = to_clk_pll(hw); + struct clk_hw *parent_hw = clk_hw_get_parent(hw); + + pll->pms.parent_rate = clk_hw_get_rate(parent_hw); + pll->pms.rate = clk_pll_recalc_rate(&pll->hw, pll->pms.parent_rate); + pll->pms.status = clk_pll_ready(pll->regmap, PLL_REG(pll->id)); + + return 0; +} + +static void clk_pll_restore_context(struct clk_hw *hw) +{ + struct clk_pll *pll = to_clk_pll(hw); + unsigned long calc_rate; + unsigned int pllr, pllr_out, pllr_count; + u8 out = 0; + + if (pll->characteristics->out) + out = pll->characteristics->out[pll->range]; + + regmap_read(pll->regmap, PLL_REG(pll->id), &pllr); + + calc_rate = (pll->pms.parent_rate / PLL_DIV(pllr)) * + (PLL_MUL(pllr, pll->layout) + 1); + pllr_count = (pllr >> PLL_COUNT_SHIFT) & PLL_MAX_COUNT; + pllr_out = (pllr >> PLL_OUT_SHIFT) & out; + + if (pll->pms.rate != calc_rate || + pll->pms.status != clk_pll_ready(pll->regmap, PLL_REG(pll->id)) || + pllr_count != PLL_MAX_COUNT || + (out && pllr_out != out)) + pr_warn("PLLAR was not configured properly by firmware\n"); +} + static const struct clk_ops pll_ops = { .prepare = clk_pll_prepare, .unprepare = clk_pll_unprepare, .is_prepared = clk_pll_is_prepared, .recalc_rate = clk_pll_recalc_rate, - .round_rate = clk_pll_round_rate, + .determine_rate = clk_pll_determine_rate, .set_rate = clk_pll_set_rate, + .save_context = clk_pll_save_context, + .restore_context = clk_pll_restore_context, }; struct clk_hw * __init |
