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

Commit 7452b219 authored by Mike Turquette's avatar Mike Turquette
Browse files

clk: core: clk_calc_new_rates handles NULL parents



It is possible to call clk_set_rate on a clock with a NULL parent.  One
such example is an adjustable-rate root clock.  Ensure that
clk_calc_new_rates does not dereference parent without checking first
and also handle the corner cases gracefully.

Reported-by: default avatarRajendra Nayak <rnayak@ti.com>
Signed-off-by: default avatarMike Turquette <mturquette@linaro.org>
parent 70d347e6
Loading
Loading
Loading
Loading
+21 −8
Original line number Original line Diff line number Diff line
@@ -763,25 +763,38 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate)
static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate)
static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate)
{
{
	struct clk *top = clk;
	struct clk *top = clk;
	unsigned long best_parent_rate = clk->parent->rate;
	unsigned long best_parent_rate;
	unsigned long new_rate;
	unsigned long new_rate;


	if (!clk->ops->round_rate && !(clk->flags & CLK_SET_RATE_PARENT)) {
	/* sanity */
	if (IS_ERR_OR_NULL(clk))
		return NULL;

	/* never propagate up to the parent */
	if (!(clk->flags & CLK_SET_RATE_PARENT)) {
		if (!clk->ops->round_rate) {
			clk->new_rate = clk->rate;
			clk->new_rate = clk->rate;
			return NULL;
			return NULL;
		} else {
			new_rate = clk->ops->round_rate(clk->hw, rate, NULL);
			goto out;
		}
	}

	/* need clk->parent from here on out */
	if (!clk->parent) {
		pr_debug("%s: %s has NULL parent\n", __func__, clk->name);
		return NULL;
	}
	}


	if (!clk->ops->round_rate && (clk->flags & CLK_SET_RATE_PARENT)) {
	if (!clk->ops->round_rate) {
		top = clk_calc_new_rates(clk->parent, rate);
		top = clk_calc_new_rates(clk->parent, rate);
		new_rate = clk->new_rate = clk->parent->new_rate;
		new_rate = clk->new_rate = clk->parent->new_rate;


		goto out;
		goto out;
	}
	}


	if (clk->flags & CLK_SET_RATE_PARENT)
	new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate);
	new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate);
	else
		new_rate = clk->ops->round_rate(clk->hw, rate, NULL);


	if (best_parent_rate != clk->parent->rate) {
	if (best_parent_rate != clk->parent->rate) {
		top = clk_calc_new_rates(clk->parent, best_parent_rate);
		top = clk_calc_new_rates(clk->parent, best_parent_rate);