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

Commit 9d3745d4 authored by Stephen Boyd's avatar Stephen Boyd
Browse files

clk: qcom: Properly change rates for ahbix clock



The ahbix clock can never be turned off in practice. To change the
rates we need to switch the mux off the M/N counter to an always on
source (XO), reprogram the M/N counter to get the rate we want and
finally switch back to the M/N counter. Add a new ops structure
for this type of clock so that we can set the rate properly.

Fixes: c99e515a "clk: qcom: Add IPQ806X LPASS clock controller (LCC) driver"
Tested-by: default avatarKenneth Westfield <kwestfie@codeaurora.org>
Signed-off-by: default avatarStephen Boyd <sboyd@codeaurora.org>
parent 65bd2004
Loading
Loading
Loading
Loading
+62 −0
Original line number Original line Diff line number Diff line
@@ -495,6 +495,57 @@ static int clk_rcg_bypass_set_rate(struct clk_hw *hw, unsigned long rate,
	return __clk_rcg_set_rate(rcg, rcg->freq_tbl);
	return __clk_rcg_set_rate(rcg, rcg->freq_tbl);
}
}


/*
 * This type of clock has a glitch-free mux that switches between the output of
 * the M/N counter and an always on clock source (XO). When clk_set_rate() is
 * called we need to make sure that we don't switch to the M/N counter if it
 * isn't clocking because the mux will get stuck and the clock will stop
 * outputting a clock. This can happen if the framework isn't aware that this
 * clock is on and so clk_set_rate() doesn't turn on the new parent. To fix
 * this we switch the mux in the enable/disable ops and reprogram the M/N
 * counter in the set_rate op. We also make sure to switch away from the M/N
 * counter in set_rate if software thinks the clock is off.
 */
static int clk_rcg_lcc_set_rate(struct clk_hw *hw, unsigned long rate,
				unsigned long parent_rate)
{
	struct clk_rcg *rcg = to_clk_rcg(hw);
	const struct freq_tbl *f;
	int ret;
	u32 gfm = BIT(10);

	f = qcom_find_freq(rcg->freq_tbl, rate);
	if (!f)
		return -EINVAL;

	/* Switch to XO to avoid glitches */
	regmap_update_bits(rcg->clkr.regmap, rcg->ns_reg, gfm, 0);
	ret = __clk_rcg_set_rate(rcg, f);
	/* Switch back to M/N if it's clocking */
	if (__clk_is_enabled(hw->clk))
		regmap_update_bits(rcg->clkr.regmap, rcg->ns_reg, gfm, gfm);

	return ret;
}

static int clk_rcg_lcc_enable(struct clk_hw *hw)
{
	struct clk_rcg *rcg = to_clk_rcg(hw);
	u32 gfm = BIT(10);

	/* Use M/N */
	return regmap_update_bits(rcg->clkr.regmap, rcg->ns_reg, gfm, gfm);
}

static void clk_rcg_lcc_disable(struct clk_hw *hw)
{
	struct clk_rcg *rcg = to_clk_rcg(hw);
	u32 gfm = BIT(10);

	/* Use XO */
	regmap_update_bits(rcg->clkr.regmap, rcg->ns_reg, gfm, 0);
}

static int __clk_dyn_rcg_set_rate(struct clk_hw *hw, unsigned long rate)
static int __clk_dyn_rcg_set_rate(struct clk_hw *hw, unsigned long rate)
{
{
	struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
	struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
@@ -543,6 +594,17 @@ const struct clk_ops clk_rcg_bypass_ops = {
};
};
EXPORT_SYMBOL_GPL(clk_rcg_bypass_ops);
EXPORT_SYMBOL_GPL(clk_rcg_bypass_ops);


const struct clk_ops clk_rcg_lcc_ops = {
	.enable = clk_rcg_lcc_enable,
	.disable = clk_rcg_lcc_disable,
	.get_parent = clk_rcg_get_parent,
	.set_parent = clk_rcg_set_parent,
	.recalc_rate = clk_rcg_recalc_rate,
	.determine_rate = clk_rcg_determine_rate,
	.set_rate = clk_rcg_lcc_set_rate,
};
EXPORT_SYMBOL_GPL(clk_rcg_lcc_ops);

const struct clk_ops clk_dyn_rcg_ops = {
const struct clk_ops clk_dyn_rcg_ops = {
	.enable = clk_enable_regmap,
	.enable = clk_enable_regmap,
	.is_enabled = clk_is_enabled_regmap,
	.is_enabled = clk_is_enabled_regmap,
+1 −0
Original line number Original line Diff line number Diff line
@@ -96,6 +96,7 @@ struct clk_rcg {


extern const struct clk_ops clk_rcg_ops;
extern const struct clk_ops clk_rcg_ops;
extern const struct clk_ops clk_rcg_bypass_ops;
extern const struct clk_ops clk_rcg_bypass_ops;
extern const struct clk_ops clk_rcg_lcc_ops;


#define to_clk_rcg(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg, clkr)
#define to_clk_rcg(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg, clkr)


+2 −3
Original line number Original line Diff line number Diff line
@@ -386,13 +386,12 @@ static struct clk_rcg ahbix_clk = {
	.freq_tbl = clk_tbl_ahbix,
	.freq_tbl = clk_tbl_ahbix,
	.clkr = {
	.clkr = {
		.enable_reg = 0x38,
		.enable_reg = 0x38,
		.enable_mask = BIT(10), /* toggle the gfmux to select mn/pxo */
		.enable_mask = BIT(11),
		.hw.init = &(struct clk_init_data){
		.hw.init = &(struct clk_init_data){
			.name = "ahbix",
			.name = "ahbix",
			.parent_names = lcc_pxo_pll4,
			.parent_names = lcc_pxo_pll4,
			.num_parents = 2,
			.num_parents = 2,
			.ops = &clk_rcg_ops,
			.ops = &clk_rcg_lcc_ops,
			.flags = CLK_SET_RATE_GATE,
		},
		},
	},
	},
};
};