Merge "clk: qcom: Add support for hardware control branch clocks"

This commit is contained in:
qctecmdr 2019-09-05 01:38:45 -07:00 • committed by Gerrit - the friendly Code Review server
commit 4ba0a09521
3 changed files with 103 additions and 20 deletions

View file

@ -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 <linux/kernel.h>
@ -8,6 +8,7 @@
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/regmap.h>
@ -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,

View file

@ -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;

View file

@ -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);
}