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

Commit 61dad289 authored by Taniya Das's avatar Taniya Das Committed by Gerrit - the friendly Code Review server
Browse files

clk: Move VDD voltage voting from core clock to top clock



Current vdd voting happens from top to core (clock on which the rate has
been requested), which has issues with sources which are slewing.
In the cases where the PLLs are slewing the RCGs should have the voltage
already maintained for the new frequency before the PLLs are slewed.
Modify clk_core_set_rate_nolock() so that it votes for all new frequency
voltages for the core clock and each of its parent clocks before physically
changing any rates.

Change-Id: Idf02bedcfb05a37e0c944ebe36d2dcf9032ae0d8
Signed-off-by: default avatarTaniya Das <tdas@codeaurora.org>
parent 3b3e314b
Loading
Loading
Loading
Loading
+86 −20
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
 * Copyright (C) 2011-2012 Linaro Ltd <mturquette@linaro.org>
 * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
 * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
@@ -67,7 +67,9 @@ struct clk_core {
	unsigned long		rate;
	unsigned long		req_rate;
	unsigned long		new_rate;
	unsigned long		old_rate;
	struct clk_core		*new_parent;
	struct clk_core		*old_parent;
	struct clk_core		*new_child;
	unsigned long		flags;
	bool			orphan;
@@ -2081,7 +2083,7 @@ static int clk_change_rate(struct clk_core *core)
	struct clk_core *parent = NULL;
	int rc = 0;

	old_rate = core->rate;
	core->old_rate = old_rate = core->rate;

	if (core->new_parent) {
		parent = core->new_parent;
@@ -2095,6 +2097,8 @@ static int clk_change_rate(struct clk_core *core)
	if (rc)
		return rc;

	core->old_parent = core->parent;

	if (core->flags & CLK_SET_RATE_UNGATE) {
		unsigned long flags;

@@ -2106,15 +2110,9 @@ static int clk_change_rate(struct clk_core *core)

	trace_clk_set_rate(core, core->new_rate);

	/* Enforce vdd requirements for new frequency. */
	if (core->prepare_count) {
		rc = clk_vote_rate_vdd(core, core->new_rate);
		if (rc)
			goto out;
	}

	if (core->new_parent && core->new_parent != core->parent) {
		old_parent = __clk_set_parent_before(core, core->new_parent);
		core->old_parent = old_parent;
		trace_clk_set_parent(core, core->new_parent);

		if (core->ops->set_rate_and_parent) {
@@ -2144,10 +2142,6 @@ static int clk_change_rate(struct clk_core *core)

	trace_clk_set_rate_complete(core, core->new_rate);

	/* Release vdd requirements for old frequency. */
	if (core->prepare_count)
		clk_unvote_rate_vdd(core, old_rate);

	core->rate = clk_recalc(core, best_parent_rate);

	if (core->flags & CLK_SET_RATE_UNGATE) {
@@ -2185,13 +2179,7 @@ static int clk_change_rate(struct clk_core *core)
	if (core->new_child)
		rc = clk_change_rate(core->new_child);

	clk_pm_runtime_put(core);
	return rc;

err_set_rate:
	if (core->prepare_count)
		clk_unvote_rate_vdd(core, core->new_rate);
out:
	clk_pm_runtime_put(core);
	return rc;
}
@@ -2223,6 +2211,74 @@ static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core,
	return ret ? 0 : req.rate;
}

static int vote_vdd_up(struct clk_core *core)
{
	struct clk_core *parent = NULL;
	int ret, cur_level, next_level;

	/* sanity */
	if (IS_ERR_OR_NULL(core))
		return 0;

	if (core->vdd_class) {
		cur_level = clk_find_vdd_level(core, core->rate);
		next_level = clk_find_vdd_level(core, core->new_rate);
		if (cur_level == next_level)
			return 0;
	}

	/* save parent rate, if it exists */
	if (core->new_parent)
		parent = core->new_parent;
	else if (core->parent)
		parent = core->parent;

	if (core->prepare_count && core->new_rate) {
		ret = clk_vote_rate_vdd(core, core->new_rate);
		if (ret)
			return ret;
	}

	vote_vdd_up(parent);

	return 0;
}

static int vote_vdd_down(struct clk_core *core)
{
	struct clk_core *parent;
	unsigned long rate;
	int cur_level, old_level;

	/* sanity */
	if (IS_ERR_OR_NULL(core))
		return 0;

	rate = core->old_rate;

	/* New rate set was a failure */
	if (DIV_ROUND_CLOSEST(core->rate, 1000) !=
		DIV_ROUND_CLOSEST(core->new_rate, 1000))
		rate = core->new_rate;

	if (core->vdd_class) {
		cur_level = clk_find_vdd_level(core, core->rate);
		old_level = clk_find_vdd_level(core, core->old_rate);
		if ((cur_level == old_level)
			|| !core->vdd_class->level_votes[old_level])
			return 0;
	}

	parent = core->old_parent;

	if (core->prepare_count && rate)
		clk_unvote_rate_vdd(core, rate);

	vote_vdd_down(parent);

	return 0;
}

static int clk_core_set_rate_nolock(struct clk_core *core,
				    unsigned long req_rate)
{
@@ -2262,16 +2318,26 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
		goto err;
	}

	/* Enforce the VDD for new frequency */
	ret = vote_vdd_up(core);
	if (ret)
		goto err;

	/* change the rates */
	ret = clk_change_rate(top);
	if (ret) {
		pr_err("%s: failed to set %s clock to run at %lu\n", __func__,
				top->name, req_rate);
		clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
		return ret;
		/* Release vdd requirements for new frequency. */
		vote_vdd_down(core);
		goto err;
	}

	core->req_rate = req_rate;
	/* Release vdd requirements for old frequency. */
	vote_vdd_down(core);

err:
	clk_pm_runtime_put(core);