mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-09 21:59:12 -04:00
Merge "clk: qcom: Add support for hardware control branch clocks"
This commit is contained in:
commit
4ba0a09521
3 changed files with 103 additions and 20 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue