diff options
author | Mike Turquette <mturquette@linaro.org> | 2012-03-26 17:45:36 -0400 |
---|---|---|
committer | Mike Turquette <mturquette@linaro.org> | 2012-04-24 19:37:37 -0400 |
commit | 7452b2191cd55fb3fd6ad65344466ddcdbe4676e (patch) | |
tree | 5ce68dcf470dd0de7bac7d354ea7be10e52467d5 /drivers/clk/clk.c | |
parent | 70d347e6cd0d2a7ecc023b44ef721bc2c2a38f22 (diff) |
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) | |||
763 | static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate) | 763 | static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate) |
764 | { | 764 | { |
765 | struct clk *top = clk; | 765 | struct clk *top = clk; |
766 | unsigned long best_parent_rate = clk->parent->rate; | 766 | unsigned long best_parent_rate; |
767 | unsigned long new_rate; | 767 | unsigned long new_rate; |
768 | 768 | ||
769 | if (!clk->ops->round_rate && !(clk->flags & CLK_SET_RATE_PARENT)) { | 769 | /* sanity */ |
770 | clk->new_rate = clk->rate; | 770 | if (IS_ERR_OR_NULL(clk)) |
771 | return NULL; | ||
772 | |||
773 | /* never propagate up to the parent */ | ||
774 | if (!(clk->flags & CLK_SET_RATE_PARENT)) { | ||
775 | if (!clk->ops->round_rate) { | ||
776 | clk->new_rate = clk->rate; | ||
777 | return NULL; | ||
778 | } else { | ||
779 | new_rate = clk->ops->round_rate(clk->hw, rate, NULL); | ||
780 | goto out; | ||
781 | } | ||
782 | } | ||
783 | |||
784 | /* need clk->parent from here on out */ | ||
785 | if (!clk->parent) { | ||
786 | pr_debug("%s: %s has NULL parent\n", __func__, clk->name); | ||
771 | return NULL; | 787 | return NULL; |
772 | } | 788 | } |
773 | 789 | ||
774 | if (!clk->ops->round_rate && (clk->flags & CLK_SET_RATE_PARENT)) { | 790 | if (!clk->ops->round_rate) { |
775 | top = clk_calc_new_rates(clk->parent, rate); | 791 | top = clk_calc_new_rates(clk->parent, rate); |
776 | new_rate = clk->new_rate = clk->parent->new_rate; | 792 | new_rate = clk->new_rate = clk->parent->new_rate; |
777 | 793 | ||
778 | goto out; | 794 | goto out; |
779 | } | 795 | } |
780 | 796 | ||
781 | if (clk->flags & CLK_SET_RATE_PARENT) | 797 | new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate); |
782 | new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate); | ||
783 | else | ||
784 | new_rate = clk->ops->round_rate(clk->hw, rate, NULL); | ||
785 | 798 | ||
786 | if (best_parent_rate != clk->parent->rate) { | 799 | if (best_parent_rate != clk->parent->rate) { |
787 | top = clk_calc_new_rates(clk->parent, best_parent_rate); | 800 | top = clk_calc_new_rates(clk->parent, best_parent_rate); |