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

Commit c544a4a9 authored by Kyle Yan's avatar Kyle Yan Committed by Gerrit - the friendly Code Review server
Browse files

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

parents 74a1b075 3f55a810
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
/*
 * Copyright (c) 2013, The Linux Foundation. All rights reserved.
 * Copyright (c) 2013, 2016, The Linux Foundation. All rights reserved.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
@@ -157,6 +157,7 @@ extern const struct clk_ops clk_dyn_rcg_ops;
 * @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
 *
 */
@@ -167,6 +168,7 @@ struct clk_rcg2 {
	const struct parent_map	*parent_map;
	const struct freq_tbl	*freq_tbl;
	unsigned long		current_freq;
	bool			enable_safe_config;
	struct clk_regmap	clkr;
};

+110 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <linux/delay.h>
#include <linux/regmap.h>
#include <linux/math64.h>
#include <linux/clk.h>

#include <asm/div64.h>

@@ -48,6 +49,14 @@
#define N_REG			0xc
#define D_REG			0x10

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);
@@ -125,6 +134,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 void clk_rcg2_clear_force_enable(struct clk_hw *hw)
{
	struct clk_rcg2 *rcg = to_clk_rcg2(hw);

	regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
					CMD_ROOT_EN, 0);
}

/*
 * Calculate m/n:d rate
 *
@@ -156,6 +194,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->cmd_rcgr + CFG_REG, &cfg);

	if (rcg->mnd_width) {
@@ -275,6 +319,15 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
	if (!f)
		return -EINVAL;

	/*
	 * 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;
	}

	return clk_rcg2_configure(rcg, f);
}

@@ -290,8 +343,65 @@ static int clk_rcg2_set_rate_and_parent(struct clk_hw *hw,
	return __clk_rcg2_set_rate(hw, rate);
}

static int clk_rcg2_enable(struct clk_hw *hw)
{
	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
	unsigned long rate = clk_get_rate(hw->clk);
	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.
	 */
	f = qcom_find_freq(rcg->freq_tbl, rate);
	if (!f)
		return -EINVAL;

	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,