From b35ea3ffcd84b314696de3cd6c217aa8ae22dcb3 Mon Sep 17 00:00:00 2001 From: Mike Tipton Date: Fri, 23 Apr 2021 14:22:21 -0700 Subject: [PATCH] clk: qcom: Fix crashes accessing divider registers There are a couple circumstances where divider registers can be accessed without first resuming their provider device. This can result in bus errors due to necessary resources being disabled. Call clk_runtime_get_regmap() from these places to ensure the device is resumed and any necessary resources are enabled before touching the registers. Change-Id: I61e2c1b78fd47891ecc5b2e8ced54124d40b69e4 Signed-off-by: Mike Tipton --- drivers/clk/qcom/clk-regmap-divider.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/clk/qcom/clk-regmap-divider.c b/drivers/clk/qcom/clk-regmap-divider.c index f144cb9ed9e9..f0cebc09e619 100644 --- a/drivers/clk/qcom/clk-regmap-divider.c +++ b/drivers/clk/qcom/clk-regmap-divider.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2014, 2017, 2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2014, 2017, 2020-2021, The Linux Foundation. All rights reserved. */ #include @@ -22,11 +22,18 @@ static long div_round_ro_rate(struct clk_hw *hw, unsigned long rate, struct clk_regmap_div *divider = to_clk_regmap_div(hw); struct clk_regmap *clkr = ÷r->clkr; u32 val; + int ret; + + ret = clk_runtime_get_regmap(clkr); + if (ret) + return ret; regmap_read(clkr->regmap, divider->reg, &val); val >>= divider->shift; val &= BIT(divider->width) - 1; + clk_runtime_put_regmap(clkr); + return divider_ro_round_rate(hw, rate, prate, NULL, divider->width, CLK_DIVIDER_ROUND_CLOSEST, val); } @@ -82,12 +89,19 @@ static long clk_regmap_div_list_rate(struct clk_hw *hw, unsigned int n, struct clk_regmap *clkr = ÷r->clkr; struct clk_regmap *parent_clkr = to_clk_regmap(parent_hw); u32 div; + int ret; + + ret = clk_runtime_get_regmap(clkr); + if (ret) + return ret; regmap_read(clkr->regmap, divider->reg, &div); div >>= divider->shift; div &= BIT(divider->width) - 1; div += 1; + clk_runtime_put_regmap(clkr); + if (parent_clkr && parent_clkr->ops && parent_clkr->ops->list_rate) return (parent_clkr->ops->list_rate(parent_hw, n, fmax) / div);