diff options
author | Mike Turquette <mturquette@linaro.org> | 2012-03-26 23:45:36 +0200 |
---|---|---|
committer | Mike Turquette <mturquette@linaro.org> | 2012-04-25 01:37:37 +0200 |
commit | 7452b2191cd55fb3fd6ad65344466ddcdbe4676e (patch) | |
tree | 5ce68dcf470dd0de7bac7d354ea7be10e52467d5 /drivers/clk/clk.c | |
parent | clk: core: remove dead code paths (diff) | |
download | linux-7452b2191cd55fb3fd6ad65344466ddcdbe4676e.tar.xz linux-7452b2191cd55fb3fd6ad65344466ddcdbe4676e.zip |
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: Rajendra Nayak <rnayak@ti.com>
Signed-off-by: Mike Turquette <mturquette@linaro.org>
Diffstat (limited to 'drivers/clk/clk.c')
-rw-r--r-- | drivers/clk/clk.c | 29 |
1 files changed, 21 insertions, 8 deletions
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 4daacf5783aa..d83a9e09e1bf 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -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) { struct clk *top = clk; - unsigned long best_parent_rate = clk->parent->rate; + unsigned long best_parent_rate; unsigned long new_rate; - if (!clk->ops->round_rate && !(clk->flags & CLK_SET_RATE_PARENT)) { - clk->new_rate = clk->rate; + /* 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; + 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); new_rate = clk->new_rate = clk->parent->new_rate; goto out; } - if (clk->flags & CLK_SET_RATE_PARENT) - new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate); - else - new_rate = clk->ops->round_rate(clk->hw, rate, NULL); + new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate); if (best_parent_rate != clk->parent->rate) { top = clk_calc_new_rates(clk->parent, best_parent_rate); |