From f9fd5b93e026449443fa5583d133bb5463d96d78 Mon Sep 17 00:00:00 2001 From: David Collins Date: Mon, 18 Mar 2019 14:09:10 -0700 Subject: [PATCH 1/2] clk: qcom: clk-rcg2: correct set rate handling for FORCE_ENABLE_RCG Currently, if clk_set_rate() is called on a disabled clock that has flags = FORCE_ENABLE_RCG, the rate is neither physically set nor properly cached in software. This means that when the clock is later enabled, its rate will not be what was previously requested. Change the handling of FORCE_ENABLE_RCG so that all clk_set_rate() calls are respected regardless of a clock's enable state. In doing so, ensure that FORCE_ENABLE_RCG and enable_safe_config are supported both individually and together. Implement the following behavior: enable_safe_config == true: enable - configure the RCG according to the cached current_freq disable - configure the RCG for CXO (safe) source set_rate - (if prepared) configure the RCG for the requested rate (if not prepared) cache the rate in current_freq flags | FORCE_ENABLE_RCG: enable - set ROOT_EN=1 to force the RCG to be enabled disable - set ROOT_EN=0 set_rate - configure the RCG for the requested rate; if disabled, then set ROOT_EN=1 before the configuration is changed and ROOT_EN=0 after enable_safe_config && (flags | FORCE_ENABLE_RCG): enable - set ROOT_EN=1 and configure the RCG according to the cached current_freq disable - configure the RCG for CXO source and set ROOT_EN=0 set_rate - (if prepared) configure the RCG for the requested rate (if not prepared) cache the rate in current_freq Change-Id: Ib7442e4a4b572ae7c567929044410e09a9886c76 Signed-off-by: David Collins --- drivers/clk/qcom/clk-rcg2.c | 62 +++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index eede9dfca08f..a1ee65e7bc05 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -175,6 +175,16 @@ static int clk_rcg2_clear_force_enable(struct clk_hw *hw) CMD_ROOT_EN, 0); } +static bool clk_rcg2_is_force_enabled(struct clk_hw *hw) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + u32 val = 0; + + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, &val); + + return val & CMD_ROOT_EN; +} + static int prepare_enable_rcg_srcs(struct clk *curr, struct clk *new) { int rc = 0; @@ -247,6 +257,7 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) struct clk_rcg2 *rcg = to_clk_rcg2(hw); const struct freq_tbl *f_curr; u32 cfg, src, hid_div, m = 0, n = 0, mode = 0, mask; + unsigned long rrate = 0; regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg); src = cfg; @@ -284,7 +295,16 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) hid_div &= mask; } - return calc_rate(parent_rate, m, n, mode, hid_div); + rrate = calc_rate(parent_rate, m, n, mode, hid_div); + + /* + * Check to cover the case when the RCG has been initialized to a + * non-CXO frequency before the clock driver has taken control of it. + */ + if (rcg->enable_safe_config && !rcg->current_freq) + rcg->current_freq = rrate; + + return rrate; } static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f, @@ -431,6 +451,7 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, const struct freq_tbl *f, *f_curr; int ret, curr_src_index, new_src_index; struct clk_hw *curr_src = NULL, *new_src = NULL; + bool force_enabled = false; switch (policy) { case FLOOR: @@ -482,7 +503,11 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, /* The RCG could currently be disabled. Enable its parents. */ ret = prepare_enable_rcg_srcs(curr_src->clk, new_src->clk); - clk_rcg2_set_force_enable(hw); + if (ret) + return ret; + force_enabled = clk_rcg2_is_force_enabled(hw); + if (!force_enabled) + clk_rcg2_set_force_enable(hw); } ret = clk_rcg2_configure(rcg, f); @@ -490,7 +515,8 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, return ret; if (rcg->flags & FORCE_ENABLE_RCG) { - clk_rcg2_clear_force_enable(hw); + if (!force_enabled) + clk_rcg2_clear_force_enable(hw); disable_unprepare_rcg_srcs(curr_src->clk, new_src->clk); } @@ -528,11 +554,10 @@ static int clk_rcg2_enable(struct clk_hw *hw) struct clk_rcg2 *rcg = to_clk_rcg2(hw); unsigned long rate; const struct freq_tbl *f; + int ret; - if (rcg->flags & FORCE_ENABLE_RCG) { + if (rcg->flags & FORCE_ENABLE_RCG) clk_rcg2_set_force_enable(hw); - return 0; - } if (!rcg->enable_safe_config) return 0; @@ -559,24 +584,27 @@ static int clk_rcg2_enable(struct clk_hw *hw) if (rate == cxo_f.freq) f = &cxo_f; - clk_rcg2_set_force_enable(hw); - clk_rcg2_configure(rcg, f); - clk_rcg2_clear_force_enable(hw); + if (!(rcg->flags & FORCE_ENABLE_RCG)) + clk_rcg2_set_force_enable(hw); - return 0; + ret = clk_rcg2_configure(rcg, f); + + if (!(rcg->flags & FORCE_ENABLE_RCG)) + clk_rcg2_clear_force_enable(hw); + + return ret; } static void clk_rcg2_disable(struct clk_hw *hw) { struct clk_rcg2 *rcg = to_clk_rcg2(hw); + int ret; - if (rcg->flags & FORCE_ENABLE_RCG) { - clk_rcg2_clear_force_enable(hw); + if (!rcg->enable_safe_config) { + if (rcg->flags & FORCE_ENABLE_RCG) + clk_rcg2_clear_force_enable(hw); return; } - - if (!rcg->enable_safe_config) - return; /* * Park the RCG at a safe configuration - sourced off the CXO. This is * needed for 2 reasons: In the case of RCGs sourcing PSCBCs, due to a @@ -594,7 +622,9 @@ static void clk_rcg2_disable(struct clk_hw *hw) * online. Therefore, the RCG can safely be switched. */ clk_rcg2_set_force_enable(hw); - clk_rcg2_configure(rcg, &cxo_f); + ret = clk_rcg2_configure(rcg, &cxo_f); + if (ret) + pr_err("%s: CXO configuration failed\n", clk_hw_get_name(hw)); clk_rcg2_clear_force_enable(hw); } From 8daaceddcef0161554f0ae800d0c6a788ba72a0b Mon Sep 17 00:00:00 2001 From: Deepak Katragadda Date: Thu, 29 Sep 2016 13:28:59 +0530 Subject: [PATCH 2/2] clk: qcom: Add support for hardware control branch clocks Add new ops to allow clock clients to enable/disable hardware dynamic gating of the clock branch. Change-Id: I14abed3827de8cefc31f3deb3c1e589136c32b8d Signed-off-by: Taniya Das Signed-off-by: Deepak Katragadda Signed-off-by: David Dai Signed-off-by: David Collins --- drivers/clk/qcom/clk-branch.c | 56 +++++++++++++++++++++++++++++++++-- drivers/clk/qcom/clk-branch.h | 5 ++-- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/drivers/clk/qcom/clk-branch.c b/drivers/clk/qcom/clk-branch.c index f869fc6aaed6..43ad05b584d7 100644 --- a/drivers/clk/qcom/clk-branch.c +++ b/drivers/clk/qcom/clk-branch.c @@ -1,6 +1,6 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2013, 2016, The Linux Foundation. All rights reserved. */ #include @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -152,6 +153,57 @@ const struct clk_ops clk_branch2_aon_ops = { }; EXPORT_SYMBOL_GPL(clk_branch2_aon_ops); +static unsigned long clk_branch2_hw_ctl_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return parent_rate; +} + +static int clk_branch2_hw_ctl_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_hw *clkp; + + clkp = clk_hw_get_parent(hw); + if (!clkp) + return -EINVAL; + + req->best_parent_hw = clkp; + req->best_parent_rate = clk_round_rate(clkp->clk, req->rate); + + return 0; +} + +static int clk_branch2_hw_ctl_enable(struct clk_hw *hw) +{ + struct clk_hw *parent = clk_hw_get_parent(hw); + + /* The parent branch clock should have been prepared prior to this. */ + if (!parent || (parent && !clk_hw_is_prepared(parent))) + return -EINVAL; + + return clk_enable_regmap(hw); +} + +static void clk_branch2_hw_ctl_disable(struct clk_hw *hw) +{ + struct clk_hw *parent = clk_hw_get_parent(hw); + + if (!parent) + return; + + clk_disable_regmap(hw); +} + +const struct clk_ops clk_branch2_hw_ctl_ops = { + .enable = clk_branch2_hw_ctl_enable, + .disable = clk_branch2_hw_ctl_disable, + .is_enabled = clk_is_enabled_regmap, + .recalc_rate = clk_branch2_hw_ctl_recalc_rate, + .determine_rate = clk_branch2_hw_ctl_determine_rate, +}; +EXPORT_SYMBOL(clk_branch2_hw_ctl_ops); + const struct clk_ops clk_branch_simple_ops = { .enable = clk_enable_regmap, .disable = clk_disable_regmap, diff --git a/drivers/clk/qcom/clk-branch.h b/drivers/clk/qcom/clk-branch.h index 17a58119165e..8f71551b2aea 100644 --- a/drivers/clk/qcom/clk-branch.h +++ b/drivers/clk/qcom/clk-branch.h @@ -1,5 +1,5 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (c) 2013, The Linux Foundation. All rights reserved. */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2013, 2016, The Linux Foundation. All rights reserved. */ #ifndef __QCOM_CLK_BRANCH_H__ #define __QCOM_CLK_BRANCH_H__ @@ -39,6 +39,7 @@ struct clk_branch { extern const struct clk_ops clk_branch_ops; extern const struct clk_ops clk_branch2_ops; +extern const struct clk_ops clk_branch2_hw_ctl_ops; extern const struct clk_ops clk_branch_simple_ops; extern const struct clk_ops clk_branch2_aon_ops;