Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 99878ea1 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "clk: qcom: clk-rcg2: Configure the RCGs to a safe frequency as needed"

parents d5f8047c 3e549714
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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), &regval);
	regmap_read(pll->clkr.regmap, PLL_MODE(pll), &regval);
	if (regval & LUCID_PCAL_DONE)
		return 0;

+6 −2
Original line number Diff line number Diff line
/* 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;
};
+130 −34
Original line number Diff line number Diff line
// 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)
{