diff options
Diffstat (limited to 'drivers/clk/tegra/clk-divider.c')
| -rw-r--r-- | drivers/clk/tegra/clk-divider.c | 64 |
1 files changed, 39 insertions, 25 deletions
diff --git a/drivers/clk/tegra/clk-divider.c b/drivers/clk/tegra/clk-divider.c index 205fe8ff63f0..37439fcb3ac0 100644 --- a/drivers/clk/tegra/clk-divider.c +++ b/drivers/clk/tegra/clk-divider.c @@ -1,17 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <linux/kernel.h> @@ -51,8 +40,13 @@ static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw, int div, mul; u64 rate = parent_rate; - reg = readl_relaxed(divider->reg) >> divider->shift; - div = reg & div_mask(divider); + reg = readl_relaxed(divider->reg); + + if ((divider->flags & TEGRA_DIVIDER_UART) && + !(reg & PERIPH_CLK_UART_DIV_ENB)) + return rate; + + div = (reg >> divider->shift) & div_mask(divider); mul = get_mul(divider); div += mul; @@ -64,23 +58,31 @@ static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw, return rate; } -static long clk_frac_div_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_frac_div_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct tegra_clk_frac_div *divider = to_clk_frac_div(hw); int div, mul; - unsigned long output_rate = *prate; + unsigned long output_rate = req->best_parent_rate; - if (!rate) - return output_rate; + if (!req->rate) { + req->rate = output_rate; - div = get_div(divider, rate, output_rate); - if (div < 0) - return *prate; + return 0; + } + + div = get_div(divider, req->rate, output_rate); + if (div < 0) { + req->rate = req->best_parent_rate; + + return 0; + } mul = get_mul(divider); - return DIV_ROUND_UP(output_rate * mul, div + mul); + req->rate = DIV_ROUND_UP(output_rate * mul, div + mul); + + return 0; } static int clk_frac_div_set_rate(struct clk_hw *hw, unsigned long rate, @@ -120,10 +122,21 @@ static int clk_frac_div_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } +static void clk_divider_restore_context(struct clk_hw *hw) +{ + struct clk_hw *parent = clk_hw_get_parent(hw); + unsigned long parent_rate = clk_hw_get_rate(parent); + unsigned long rate = clk_hw_get_rate(hw); + + if (clk_frac_div_set_rate(hw, rate, parent_rate) < 0) + WARN_ON(1); +} + const struct clk_ops tegra_clk_frac_div_ops = { .recalc_rate = clk_frac_div_recalc_rate, .set_rate = clk_frac_div_set_rate, - .round_rate = clk_frac_div_round_rate, + .determine_rate = clk_frac_div_determine_rate, + .restore_context = clk_divider_restore_context, }; struct clk *tegra_clk_register_divider(const char *name, @@ -175,6 +188,7 @@ struct clk *tegra_clk_register_mc(const char *name, const char *parent_name, void __iomem *reg, spinlock_t *lock) { return clk_register_divider_table(NULL, name, parent_name, - CLK_IS_CRITICAL, reg, 16, 1, 0, + CLK_IS_CRITICAL, + reg, 16, 1, CLK_DIVIDER_READ_ONLY, mc_div_table, lock); } |
