mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-15 01:01:56 -04:00
Merge "clk: qcom: clk-rcg2: Configure the RCGs to a safe frequency as needed"
This commit is contained in:
commit
99878ea10d
3 changed files with 138 additions and 38 deletions
|
|
@ -157,7 +157,7 @@ EXPORT_SYMBOL_GPL(clk_alpha_pll_regs);
|
|||
|
||||
/* LUCID PLL specific settings and offsets */
|
||||
#define LUCID_PLL_CAL_VAL 0x44
|
||||
#define LUCID_PCAL_DONE BIT(26)
|
||||
#define LUCID_PCAL_DONE BIT(27)
|
||||
|
||||
/* ZONDA PLL specific offsets */
|
||||
#define ZONDA_PLL_OUT_MASK 0xF
|
||||
|
|
@ -1614,7 +1614,7 @@ static int alpha_pll_lucid_prepare(struct clk_hw *hw)
|
|||
int ret;
|
||||
|
||||
/* Return early if calibration is not needed. */
|
||||
regmap_read(pll->clkr.regmap, PLL_STATUS(pll), ®val);
|
||||
regmap_read(pll->clkr.regmap, PLL_MODE(pll), ®val);
|
||||
if (regval & LUCID_PCAL_DONE)
|
||||
return 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2013, 2018, The Linux Foundation. All rights reserved. */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2013, 2016-2018, The Linux Foundation. All rights reserved. */
|
||||
|
||||
#ifndef __QCOM_CLK_RCG_H__
|
||||
#define __QCOM_CLK_RCG_H__
|
||||
|
|
@ -137,6 +137,8 @@ extern const struct clk_ops clk_dyn_rcg_ops;
|
|||
* @safe_src_index: safe src index value
|
||||
* @parent_map: map from software's parent index to hardware's src_sel field
|
||||
* @freq_tbl: frequency table
|
||||
* @current_freq: last cached frequency when using branches with shared RCGs
|
||||
* @enable_safe_config: When set, the RCG is parked at CXO when it's disabled
|
||||
* @clkr: regmap clock handle
|
||||
* @cfg_off: defines the cfg register offset from the CMD_RCGR + CFG_REG
|
||||
*/
|
||||
|
|
@ -147,6 +149,8 @@ struct clk_rcg2 {
|
|||
u8 safe_src_index;
|
||||
const struct parent_map *parent_map;
|
||||
const struct freq_tbl *freq_tbl;
|
||||
unsigned long current_freq;
|
||||
bool enable_safe_config;
|
||||
struct clk_regmap clkr;
|
||||
u8 cfg_off;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2013, 2018, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2013, 2016-2018, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regmap.h>
|
||||
|
|
@ -59,6 +60,14 @@ enum freq_policy {
|
|||
CEIL,
|
||||
};
|
||||
|
||||
static struct freq_tbl cxo_f = {
|
||||
.freq = 19200000,
|
||||
.src = 0,
|
||||
.pre_div = 1,
|
||||
.m = 0,
|
||||
.n = 0,
|
||||
};
|
||||
|
||||
static int clk_rcg2_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
||||
|
|
@ -136,6 +145,35 @@ static int clk_rcg2_set_parent(struct clk_hw *hw, u8 index)
|
|||
return update_config(rcg);
|
||||
}
|
||||
|
||||
static int clk_rcg2_set_force_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
||||
int ret = 0, count = 500;
|
||||
|
||||
ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
|
||||
CMD_ROOT_EN, CMD_ROOT_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (; count > 0; count--) {
|
||||
if (clk_rcg2_is_enabled(hw))
|
||||
return ret;
|
||||
/* Delay for 1usec and retry polling the status bit */
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
WARN(1, "%s: rcg didn't turn on.", clk_hw_get_name(hw));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int clk_rcg2_clear_force_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
||||
|
||||
return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
|
||||
CMD_ROOT_EN, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate m/n:d rate
|
||||
*
|
||||
|
|
@ -167,6 +205,12 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
|||
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
||||
u32 cfg, hid_div, m = 0, n = 0, mode = 0, mask;
|
||||
|
||||
if (rcg->enable_safe_config && !clk_hw_is_prepared(hw)) {
|
||||
if (!rcg->current_freq)
|
||||
rcg->current_freq = cxo_f.freq;
|
||||
return rcg->current_freq;
|
||||
}
|
||||
|
||||
regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg);
|
||||
|
||||
if (rcg->mnd_width) {
|
||||
|
|
@ -309,6 +353,7 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
{
|
||||
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
||||
const struct freq_tbl *f;
|
||||
int ret;
|
||||
|
||||
switch (policy) {
|
||||
case FLOOR:
|
||||
|
|
@ -324,7 +369,22 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
if (!f)
|
||||
return -EINVAL;
|
||||
|
||||
return clk_rcg2_configure(rcg, f);
|
||||
/*
|
||||
* Return if the RCG is currently disabled. This configuration update
|
||||
* will happen as part of the RCG enable sequence.
|
||||
*/
|
||||
if (rcg->enable_safe_config && !clk_hw_is_prepared(hw)) {
|
||||
rcg->current_freq = rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = clk_rcg2_configure(rcg, f);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Update current frequency with the requested frequency. */
|
||||
rcg->current_freq = rate;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
|
|
@ -351,8 +411,75 @@ static int clk_rcg2_set_floor_rate_and_parent(struct clk_hw *hw,
|
|||
return __clk_rcg2_set_rate(hw, rate, FLOOR);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (!rcg->enable_safe_config)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Switch from CXO to the stashed mux selection. Force enable and
|
||||
* disable the RCG while configuring it to safeguard against any update
|
||||
* signal coming from the downstream clock. The current parent has
|
||||
* already been prepared and enabled at this point, and the CXO source
|
||||
* is always on while APPS is online. Therefore, the RCG can safely be
|
||||
* switched.
|
||||
*/
|
||||
rate = rcg->current_freq;
|
||||
f = qcom_find_freq(rcg->freq_tbl, rate);
|
||||
if (!f)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* If CXO is not listed as a supported frequency in the frequency
|
||||
* table, the above API would return the lowest supported frequency
|
||||
* instead. This will lead to incorrect configuration of the RCG.
|
||||
* Check if the RCG rate is CXO and configure it accordingly.
|
||||
*/
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clk_rcg2_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
||||
|
||||
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
|
||||
* default HW behavior, the RCG will turn on when its corresponding
|
||||
* GDSC is enabled. We might also have cases when the RCG might be left
|
||||
* enabled without the overlying SW knowing about it. This results from
|
||||
* hard to track cases of downstream clocks being left enabled. In both
|
||||
* these cases, scaling the RCG will fail since it's enabled but with
|
||||
* its sources cut off.
|
||||
*
|
||||
* Save mux select and switch to CXO. Force enable and disable the RCG
|
||||
* while configuring it to safeguard against any update signal coming
|
||||
* from the downstream clock. The current parent is still prepared and
|
||||
* enabled at this point, and the CXO source is always on while APPS is
|
||||
* online. Therefore, the RCG can safely be switched.
|
||||
*/
|
||||
clk_rcg2_set_force_enable(hw);
|
||||
clk_rcg2_configure(rcg, &cxo_f);
|
||||
clk_rcg2_clear_force_enable(hw);
|
||||
}
|
||||
|
||||
const struct clk_ops clk_rcg2_ops = {
|
||||
.is_enabled = clk_rcg2_is_enabled,
|
||||
.enable = clk_rcg2_enable,
|
||||
.disable = clk_rcg2_disable,
|
||||
.get_parent = clk_rcg2_get_parent,
|
||||
.set_parent = clk_rcg2_set_parent,
|
||||
.recalc_rate = clk_rcg2_recalc_rate,
|
||||
|
|
@ -805,37 +932,6 @@ const struct clk_ops clk_gfx3d_ops = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(clk_gfx3d_ops);
|
||||
|
||||
static int clk_rcg2_set_force_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
||||
const char *name = clk_hw_get_name(hw);
|
||||
int ret, count;
|
||||
|
||||
ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
|
||||
CMD_ROOT_EN, CMD_ROOT_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* wait for RCG to turn ON */
|
||||
for (count = 500; count > 0; count--) {
|
||||
if (clk_rcg2_is_enabled(hw))
|
||||
return 0;
|
||||
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
pr_err("%s: RCG did not turn on\n", name);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int clk_rcg2_clear_force_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
||||
|
||||
return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
|
||||
CMD_ROOT_EN, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
clk_rcg2_shared_force_enable_clear(struct clk_hw *hw, const struct freq_tbl *f)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue