aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2011-02-12 21:21:47 -0500
committerColin Cross <ccross@android.com>2011-02-22 14:22:34 -0500
commit421186e71000c067c2687baeffde62954a80cdcc (patch)
tree2db6e9735fbaa05bd731f5dc419f4194d850c62e
parent78f379b574dcbe656fa21ea72e95f8dff232e233 (diff)
ARM: tegra: clock: Round rate before setting rate
Call the clock's round_rate op, if it exists, before calling the set_rate op. This will help later when dvfs is added, dvfs needs to know what the final rate will be before the frequency changes. Also requires fixes to the round rate functions to ensure calling round rate and then set rate will not cause the frequency to be rounded down twice. When picking clock divider values, the clock framework picks the closest frequency that is lower than the requested frequency. If the new frequency calculated from the divider value is rounded down, and then passed to set_rate, it will get rounded down again, possibly resulting in a frequency two steps lower than the original requested frequency. Fix the problem by rounding up when calculating the frequency coming out of a clock divider, so if that frequency is requested again, the same divider value will be picked. Signed-off-by: Colin Cross <ccross@android.com> Acked-by: Olof Johansson <olof@lixom.net>
-rw-r--r--arch/arm/mach-tegra/clock.c12
-rw-r--r--arch/arm/mach-tegra/tegra2_clocks.c8
2 files changed, 16 insertions, 4 deletions
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index 165aa9c748f6..e028320ab423 100644
--- a/arch/arm/mach-tegra/clock.c
+++ b/arch/arm/mach-tegra/clock.c
@@ -86,6 +86,7 @@ static unsigned long clk_predict_rate_from_parent(struct clk *c, struct clk *p)
86 86
87 if (c->mul != 0 && c->div != 0) { 87 if (c->mul != 0 && c->div != 0) {
88 rate *= c->mul; 88 rate *= c->mul;
89 rate += c->div - 1; /* round up */
89 do_div(rate, c->div); 90 do_div(rate, c->div);
90 } 91 }
91 92
@@ -240,12 +241,23 @@ EXPORT_SYMBOL(clk_get_parent);
240 241
241int clk_set_rate_locked(struct clk *c, unsigned long rate) 242int clk_set_rate_locked(struct clk *c, unsigned long rate)
242{ 243{
244 long new_rate;
245
243 if (!c->ops || !c->ops->set_rate) 246 if (!c->ops || !c->ops->set_rate)
244 return -ENOSYS; 247 return -ENOSYS;
245 248
246 if (rate > c->max_rate) 249 if (rate > c->max_rate)
247 rate = c->max_rate; 250 rate = c->max_rate;
248 251
252 if (c->ops && c->ops->round_rate) {
253 new_rate = c->ops->round_rate(c, rate);
254
255 if (new_rate < 0)
256 return new_rate;
257
258 rate = new_rate;
259 }
260
249 return c->ops->set_rate(c, rate); 261 return c->ops->set_rate(c, rate);
250} 262}
251 263
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 2ca8b74ec07e..73e112f12695 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -898,9 +898,9 @@ static long tegra2_pll_div_clk_round_rate(struct clk *c, unsigned long rate)
898 divider = clk_div71_get_divider(parent_rate, rate); 898 divider = clk_div71_get_divider(parent_rate, rate);
899 if (divider < 0) 899 if (divider < 0)
900 return divider; 900 return divider;
901 return parent_rate * 2 / (divider + 2); 901 return DIV_ROUND_UP(parent_rate * 2, divider + 2);
902 } else if (c->flags & DIV_2) { 902 } else if (c->flags & DIV_2) {
903 return parent_rate / 2; 903 return DIV_ROUND_UP(parent_rate, 2);
904 } 904 }
905 return -EINVAL; 905 return -EINVAL;
906} 906}
@@ -1092,12 +1092,12 @@ static long tegra2_periph_clk_round_rate(struct clk *c,
1092 if (divider < 0) 1092 if (divider < 0)
1093 return divider; 1093 return divider;
1094 1094
1095 return parent_rate * 2 / (divider + 2); 1095 return DIV_ROUND_UP(parent_rate * 2, divider + 2);
1096 } else if (c->flags & DIV_U16) { 1096 } else if (c->flags & DIV_U16) {
1097 divider = clk_div16_get_divider(parent_rate, rate); 1097 divider = clk_div16_get_divider(parent_rate, rate);
1098 if (divider < 0) 1098 if (divider < 0)
1099 return divider; 1099 return divider;
1100 return parent_rate / (divider + 1); 1100 return DIV_ROUND_UP(parent_rate, divider + 1);
1101 } 1101 }
1102 return -EINVAL; 1102 return -EINVAL;
1103} 1103}