aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuennadi Liakhovetski <g.liakhovetski@gmx.de>2013-04-05 06:00:36 -0400
committerSimon Horman <horms+renesas@verge.net.au>2013-06-07 01:24:50 -0400
commit73107925f4b45b81ea4732475280502fefd35efa (patch)
tree97e8b7584ecdde8c2a4bf930a730028c61c4b985
parentaa9c185bbcd9cffdb0cda1ad24edd801f70178c5 (diff)
ARM: shmobile: sh73a0: add support for adjusting CPU frequency
On SH73A0 the output of PLL0 is supplied to two dividers, feeding clock to the CPU core and SGX. Lower CPU frequencies allow the use of lower supply voltages and thus reduce power consumption. Signed-off-by: Guennadi Liakhovetski <g.liakhovetski+renesas@gmail.com> Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
-rw-r--r--arch/arm/mach-shmobile/clock-sh73a0.c95
1 files changed, 93 insertions, 2 deletions
diff --git a/arch/arm/mach-shmobile/clock-sh73a0.c b/arch/arm/mach-shmobile/clock-sh73a0.c
index 784fbaa4cc55..acb9e0970739 100644
--- a/arch/arm/mach-shmobile/clock-sh73a0.c
+++ b/arch/arm/mach-shmobile/clock-sh73a0.c
@@ -228,6 +228,11 @@ enum { DIV4_I, DIV4_ZG, DIV4_M3, DIV4_B, DIV4_M1, DIV4_M2,
228 228
229static struct clk div4_clks[DIV4_NR] = { 229static struct clk div4_clks[DIV4_NR] = {
230 [DIV4_I] = DIV4(FRQCRA, 20, 0xdff, CLK_ENABLE_ON_INIT), 230 [DIV4_I] = DIV4(FRQCRA, 20, 0xdff, CLK_ENABLE_ON_INIT),
231 /*
232 * ZG clock is dividing PLL0 frequency to supply SGX. Make sure not to
233 * exceed maximum frequencies of 201.5MHz for VDD_DVFS=1.175 and
234 * 239.2MHz for VDD_DVFS=1.315V.
235 */
231 [DIV4_ZG] = SH_CLK_DIV4(&pll0_clk, FRQCRA, 16, 0xd7f, CLK_ENABLE_ON_INIT), 236 [DIV4_ZG] = SH_CLK_DIV4(&pll0_clk, FRQCRA, 16, 0xd7f, CLK_ENABLE_ON_INIT),
232 [DIV4_M3] = DIV4(FRQCRA, 12, 0x1dff, CLK_ENABLE_ON_INIT), 237 [DIV4_M3] = DIV4(FRQCRA, 12, 0x1dff, CLK_ENABLE_ON_INIT),
233 [DIV4_B] = DIV4(FRQCRA, 8, 0xdff, CLK_ENABLE_ON_INIT), 238 [DIV4_B] = DIV4(FRQCRA, 8, 0xdff, CLK_ENABLE_ON_INIT),
@@ -252,6 +257,85 @@ static struct clk twd_clk = {
252 .ops = &twd_clk_ops, 257 .ops = &twd_clk_ops,
253}; 258};
254 259
260static int (*div4_set_rate)(struct clk *clk, unsigned long rate);
261static unsigned long (*div4_recalc)(struct clk *clk);
262static long (*div4_round_rate)(struct clk *clk, unsigned long rate);
263
264static int zclk_set_rate(struct clk *clk, unsigned long rate)
265{
266 int ret;
267
268 if (!clk->parent || !__clk_get(clk->parent))
269 return -ENODEV;
270
271 if (readl(FRQCRB) & (1 << 31))
272 return -EBUSY;
273
274 if (rate == clk_get_rate(clk->parent)) {
275 /* 1:1 - switch off divider */
276 __raw_writel(__raw_readl(FRQCRB) & ~(1 << 28), FRQCRB);
277 /* nullify the divider to prepare for the next time */
278 ret = div4_set_rate(clk, rate / 2);
279 if (!ret)
280 ret = frqcr_kick();
281 if (ret > 0)
282 ret = 0;
283 } else {
284 /* Enable the divider */
285 __raw_writel(__raw_readl(FRQCRB) | (1 << 28), FRQCRB);
286
287 ret = frqcr_kick();
288 if (ret >= 0)
289 /*
290 * set the divider - call the DIV4 method, it will kick
291 * FRQCRB too
292 */
293 ret = div4_set_rate(clk, rate);
294 if (ret < 0)
295 goto esetrate;
296 }
297
298esetrate:
299 __clk_put(clk->parent);
300 return ret;
301}
302
303static long zclk_round_rate(struct clk *clk, unsigned long rate)
304{
305 unsigned long div_freq = div4_round_rate(clk, rate),
306 parent_freq = clk_get_rate(clk->parent);
307
308 if (rate > div_freq && abs(parent_freq - rate) < rate - div_freq)
309 return parent_freq;
310
311 return div_freq;
312}
313
314static unsigned long zclk_recalc(struct clk *clk)
315{
316 /*
317 * Must recalculate frequencies in case PLL0 has been changed, even if
318 * the divisor is unused ATM!
319 */
320 unsigned long div_freq = div4_recalc(clk);
321
322 if (__raw_readl(FRQCRB) & (1 << 28))
323 return div_freq;
324
325 return clk_get_rate(clk->parent);
326}
327
328static void zclk_extend(void)
329{
330 /* We extend the DIV4 clock with a 1:1 pass-through case */
331 div4_set_rate = div4_clks[DIV4_Z].ops->set_rate;
332 div4_round_rate = div4_clks[DIV4_Z].ops->round_rate;
333 div4_recalc = div4_clks[DIV4_Z].ops->recalc;
334 div4_clks[DIV4_Z].ops->set_rate = zclk_set_rate;
335 div4_clks[DIV4_Z].ops->round_rate = zclk_round_rate;
336 div4_clks[DIV4_Z].ops->recalc = zclk_recalc;
337}
338
255enum { DIV6_VCK1, DIV6_VCK2, DIV6_VCK3, DIV6_ZB1, 339enum { DIV6_VCK1, DIV6_VCK2, DIV6_VCK3, DIV6_ZB1,
256 DIV6_FLCTL, DIV6_SDHI0, DIV6_SDHI1, DIV6_SDHI2, 340 DIV6_FLCTL, DIV6_SDHI0, DIV6_SDHI1, DIV6_SDHI2,
257 DIV6_FSIA, DIV6_FSIB, DIV6_SUB, 341 DIV6_FSIA, DIV6_FSIB, DIV6_SUB,
@@ -450,7 +534,7 @@ static struct clk *late_main_clks[] = {
450}; 534};
451 535
452enum { MSTP001, 536enum { MSTP001,
453 MSTP129, MSTP128, MSTP127, MSTP126, MSTP125, MSTP118, MSTP116, MSTP100, 537 MSTP129, MSTP128, MSTP127, MSTP126, MSTP125, MSTP118, MSTP116, MSTP112, MSTP100,
454 MSTP219, MSTP218, MSTP217, 538 MSTP219, MSTP218, MSTP217,
455 MSTP207, MSTP206, MSTP204, MSTP203, MSTP202, MSTP201, MSTP200, 539 MSTP207, MSTP206, MSTP204, MSTP203, MSTP202, MSTP201, MSTP200,
456 MSTP331, MSTP329, MSTP328, MSTP325, MSTP323, MSTP322, 540 MSTP331, MSTP329, MSTP328, MSTP325, MSTP323, MSTP322,
@@ -471,6 +555,7 @@ static struct clk mstp_clks[MSTP_NR] = {
471 [MSTP125] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR1, 25, 0), /* TMU0 */ 555 [MSTP125] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR1, 25, 0), /* TMU0 */
472 [MSTP118] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 18, 0), /* DSITX0 */ 556 [MSTP118] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 18, 0), /* DSITX0 */
473 [MSTP116] = MSTP(&div4_clks[DIV4_HP], SMSTPCR1, 16, 0), /* IIC0 */ 557 [MSTP116] = MSTP(&div4_clks[DIV4_HP], SMSTPCR1, 16, 0), /* IIC0 */
558 [MSTP112] = MSTP(&div4_clks[DIV4_ZG], SMSTPCR1, 12, 0), /* SGX */
474 [MSTP100] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 0, 0), /* LCDC0 */ 559 [MSTP100] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 0, 0), /* LCDC0 */
475 [MSTP219] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 19, 0), /* SCIFA7 */ 560 [MSTP219] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 19, 0), /* SCIFA7 */
476 [MSTP218] = MSTP(&div4_clks[DIV4_HP], SMSTPCR2, 18, 0), /* SY-DMAC */ 561 [MSTP218] = MSTP(&div4_clks[DIV4_HP], SMSTPCR2, 18, 0), /* SY-DMAC */
@@ -513,6 +598,9 @@ static struct clk_lookup lookups[] = {
513 CLKDEV_CON_ID("r_clk", &r_clk), 598 CLKDEV_CON_ID("r_clk", &r_clk),
514 CLKDEV_DEV_ID("smp_twd", &twd_clk), /* smp_twd */ 599 CLKDEV_DEV_ID("smp_twd", &twd_clk), /* smp_twd */
515 600
601 /* DIV4 clocks */
602 CLKDEV_DEV_ID("cpufreq-cpu0", &div4_clks[DIV4_Z]),
603
516 /* DIV6 clocks */ 604 /* DIV6 clocks */
517 CLKDEV_CON_ID("vck1_clk", &div6_clks[DIV6_VCK1]), 605 CLKDEV_CON_ID("vck1_clk", &div6_clks[DIV6_VCK1]),
518 CLKDEV_CON_ID("vck2_clk", &div6_clks[DIV6_VCK2]), 606 CLKDEV_CON_ID("vck2_clk", &div6_clks[DIV6_VCK2]),
@@ -604,8 +692,11 @@ void __init sh73a0_clock_init(void)
604 for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++) 692 for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++)
605 ret = clk_register(main_clks[k]); 693 ret = clk_register(main_clks[k]);
606 694
607 if (!ret) 695 if (!ret) {
608 ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table); 696 ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table);
697 if (!ret)
698 zclk_extend();
699 }
609 700
610 if (!ret) 701 if (!ret)
611 ret = sh_clk_div6_reparent_register(div6_clks, DIV6_NR); 702 ret = sh_clk_div6_reparent_register(div6_clks, DIV6_NR);