aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk
diff options
context:
space:
mode:
authorMike Turquette <mturquette@linaro.org>2012-03-26 17:45:36 -0400
committerMike Turquette <mturquette@linaro.org>2012-04-24 19:37:37 -0400
commit7452b2191cd55fb3fd6ad65344466ddcdbe4676e (patch)
tree5ce68dcf470dd0de7bac7d354ea7be10e52467d5 /drivers/clk
parent70d347e6cd0d2a7ecc023b44ef721bc2c2a38f22 (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')
-rw-r--r--drivers/clk/clk.c29
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)
763static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate) 763static 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);