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

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

Merge "clk: qcom: Add support for hardware control branch clocks"

parents 6e52450b 8daacedd
Loading
Loading
Loading
Loading
+54 −2
Original line number Diff line number Diff line
// 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,
+3 −2
Original line number Diff line number Diff line
/* 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;

+46 −16
Original line number Diff line number Diff line
@@ -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,6 +503,10 @@ 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);
		if (ret)
			return ret;
		force_enabled = clk_rcg2_is_force_enabled(hw);
		if (!force_enabled)
			clk_rcg2_set_force_enable(hw);
	}

@@ -490,6 +515,7 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
		return ret;

	if (rcg->flags & FORCE_ENABLE_RCG) {
		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;

	if (!(rcg->flags & FORCE_ENABLE_RCG))
		clk_rcg2_set_force_enable(hw);
	clk_rcg2_configure(rcg, f);

	ret = clk_rcg2_configure(rcg, f);

	if (!(rcg->flags & FORCE_ENABLE_RCG))
		clk_rcg2_clear_force_enable(hw);

	return 0;
	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) {
	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);
}