diff --git a/drivers/clk/qcom/clk-branch.c b/drivers/clk/qcom/clk-branch.c index 8eaaed476c26..d19bb2e7f5bb 100644 --- a/drivers/clk/qcom/clk-branch.c +++ b/drivers/clk/qcom/clk-branch.c @@ -73,9 +73,25 @@ static bool clk_branch2_check_halt(const struct clk_branch *br, bool enabling) } } +static int get_branch_timeout(const struct clk_branch *br) +{ + int rate, period_us, timeout; + + /* + * The time it takes a clock branch to toggle is roughly 3 clock cycles. + */ + rate = clk_hw_get_rate(&br->clkr.hw); + period_us = 1000000 / rate; + timeout = 3 * period_us; + + return max(timeout, 200); +} + static int clk_branch_wait(const struct clk_branch *br, bool enabling, bool (check_halt)(const struct clk_branch *, bool)) { + int timeout, count; + bool voted = br->halt_check & BRANCH_VOTED; /* * Skip checking halt bit if we're explicitly ignoring the bit or the @@ -89,15 +105,15 @@ static int clk_branch_wait(const struct clk_branch *br, bool enabling, } else if (br->halt_check == BRANCH_HALT_ENABLE || br->halt_check == BRANCH_HALT || (enabling && voted)) { - int count = 200; + timeout = get_branch_timeout(br); - while (count-- > 0) { + for (count = timeout; count > 0; count--) { if (check_halt(br, enabling)) return 0; udelay(1); } - WARN_CLK((struct clk_hw *)&br->clkr.hw, 1, "status stuck at 'o%s'", - enabling ? "ff" : "n"); + WARN_CLK((struct clk_hw *)&br->clkr.hw, 1, "status stuck at 'o%s' after %d us", + enabling ? "ff" : "n", timeout); return -EBUSY; } return 0; diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h index fa1e8533ba35..e3f68354e85b 100644 --- a/drivers/clk/qcom/clk-rcg.h +++ b/drivers/clk/qcom/clk-rcg.h @@ -154,6 +154,7 @@ struct clk_rcg2 { u8 safe_src_index; const struct parent_map *parent_map; const struct freq_tbl *freq_tbl; + unsigned long configured_freq; unsigned long current_freq; bool enable_safe_config; struct clk_regmap clkr; diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index ca1bb000a3af..6c6d2c4e965d 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2013, 2016-2018, 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -118,9 +119,25 @@ static u8 clk_rcg2_get_parent(struct clk_hw *hw) return __clk_rcg2_get_parent(hw, cfg); } +static int get_update_timeout(const struct clk_rcg2 *rcg) +{ + int timeout = 0; + + /* + * The time it takes an RCG to update is roughly 3 clock cycles of the + * old and new clock rates. + */ + if (rcg->current_freq) + timeout += 3 * (1000000 / rcg->current_freq); + if (rcg->configured_freq) + timeout += 3 * (1000000 / rcg->configured_freq); + + return max(timeout, 500); +} + static int update_config(struct clk_rcg2 *rcg) { - int count, ret; + int timeout, count, ret; u32 cmd; struct clk_hw *hw = &rcg->clkr.hw; @@ -129,8 +146,10 @@ static int update_config(struct clk_rcg2 *rcg) if (ret) return ret; + timeout = get_update_timeout(rcg); + /* Wait for update to take effect */ - for (count = 500; count > 0; count--) { + for (count = timeout; count > 0; count--) { ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, &cmd); if (ret) return ret; @@ -139,7 +158,7 @@ static int update_config(struct clk_rcg2 *rcg) udelay(1); } - WARN_CLK(hw, 1, "rcg didn't update its configuration."); + WARN_CLK(hw, 1, "rcg didn't update its configuration after %d us.", timeout); return -EBUSY; } @@ -487,6 +506,8 @@ static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) if (ret) return ret; + rcg->configured_freq = f->freq; + return update_config(rcg); }