diff options
author | Alex Frid <afrid@nvidia.com> | 2011-05-03 00:51:06 -0700 |
---|---|---|
committer | Varun Colbert <vcolbert@nvidia.com> | 2011-05-13 18:42:18 -0700 |
commit | f1e0b9de06c999a8129aa2d129402d0d19be6fa3 (patch) | |
tree | 010323115b5888908759e1e4f42b72d7ceeb54a7 | |
parent | 2803bf02c3cba4bd117e5a51db336de513f1174c (diff) |
ARM: tegra: clock: Support Tegra3 CPU clock fractional divider
Added support for Tegra3 CPU super-clock fractional 7.1 divider: use
it to adjust CPU rate, when super-clock parent is fixed rate PLL (for
other parent PLLs with adjustable frequency set divider 1:1).
Bug 821438
Change-Id: Ib8342330d103beb535af4d74ea51c46b9e25dc30
Reviewed-on: http://git-master/r/31219
Reviewed-by: Aleksandr Frid <afrid@nvidia.com>
Tested-by: Aleksandr Frid <afrid@nvidia.com>
Reviewed-by: Scott Williams <scwilliams@nvidia.com>
Reviewed-by: Narendra Damahe <ndamahe@nvidia.com>
Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>
Tested-by: Diwakar Tundlam <dtundlam@nvidia.com>
-rw-r--r-- | arch/arm/mach-tegra/tegra3_clocks.c | 76 |
1 files changed, 54 insertions, 22 deletions
diff --git a/arch/arm/mach-tegra/tegra3_clocks.c b/arch/arm/mach-tegra/tegra3_clocks.c index 6731493cb487..5fda1706103d 100644 --- a/arch/arm/mach-tegra/tegra3_clocks.c +++ b/arch/arm/mach-tegra/tegra3_clocks.c @@ -196,6 +196,8 @@ #define SUPER_IDLE_SOURCE_SHIFT 0 #define SUPER_CLK_DIVIDER 0x04 +#define SUPER_CLOCK_DIV_U71_SHIFT 16 +#define SUPER_CLOCK_DIV_U71_MASK (0xff << SUPER_CLOCK_DIV_U71_SHIFT) #define BUS_CLK_DISABLE (1<<3) #define BUS_CLK_DIV_MASK 0x3 @@ -530,10 +532,12 @@ static struct clk_ops tegra_pll_ref_ops = { }; /* super clock functions */ -/* "super clocks" on tegra have two-stage muxes and a clock skipping - * super divider. We will ignore the clock skipping divider, since we - * can't lower the voltage when using the clock skip, but we can if we - * lower the PLL frequency. +/* "super clocks" on tegra3 have two-stage muxes, fractional 7.1 divider and + * clock skipping super divider. We will ignore the clock skipping divider, + * since we can't lower the voltage when using the clock skip, but we can if + * we lower the PLL frequency. We will use 7.1 divider for CPU super-clock + * only when its parent is a fixed rate PLL, since we can't change PLL rate + * in this case. */ static void tegra3_super_clk_init(struct clk *c) { @@ -556,11 +560,20 @@ static void tegra3_super_clk_init(struct clk *c) } BUG_ON(sel->input == NULL); c->parent = sel->input; + + if (c->flags & DIV_U71) { + val = clk_readl(c->reg + SUPER_CLK_DIVIDER); + val &= SUPER_CLOCK_DIV_U71_MASK; + clk_writel(val, c->reg + SUPER_CLK_DIVIDER); + c->div = (val >> SUPER_CLOCK_DIV_U71_SHIFT) + 2; + c->mul = 2; + } + else + clk_writel(0, c->reg + SUPER_CLK_DIVIDER); } static int tegra3_super_clk_enable(struct clk *c) { - clk_writel(0, c->reg + SUPER_CLK_DIVIDER); return 0; } @@ -613,14 +626,25 @@ static int tegra3_super_clk_set_parent(struct clk *c, struct clk *p) } /* - * Super clocks have "clock skippers" instead of dividers. Dividing using - * a clock skipper does not allow the voltage to be scaled down, so instead - * adjust the rate of the parent clock. This requires that the parent of a - * super clock have no other children, otherwise the rate will change - * underneath the other children. + * Do not use super clocks "skippers", since dividing using a clock skipper + * does not allow the voltage to be scaled down. Instead adjust the rate of + * the parent clock. This requires that the parent of a super clock have no + * other children, otherwise the rate will change underneath the other + * children. Special case: if fixed rate PLL is CPU super clock parent the + * rate of this PLL can't be changed, and it has many other children. In + * this case use 7.1 fractional divider to adjust the super clock rate. */ static int tegra3_super_clk_set_rate(struct clk *c, unsigned long rate) { + if ((c->flags & DIV_U71) && (c->parent->flags & PLL_FIXED)) { + int div = clk_div71_get_divider(c->parent->u.pll.fixed_rate, + rate, c->flags, ROUND_DIVIDER_DOWN); + clk_writel(div << SUPER_CLOCK_DIV_U71_SHIFT, + c->reg + SUPER_CLK_DIVIDER); + c->div = div + 2; + c->mul = 2; + return 0; + } return clk_set_rate(c->parent, rate); } @@ -659,6 +683,7 @@ static void tegra3_cpu_clk_disable(struct clk *c) static int tegra3_cpu_clk_set_rate(struct clk *c, unsigned long rate) { int ret; + unsigned int backup_rate; /* * Take an extra reference to the main pll so it doesn't turn * off when we move the cpu off of it @@ -671,7 +696,12 @@ static int tegra3_cpu_clk_set_rate(struct clk *c, unsigned long rate) goto out; } - if (rate == clk_get_rate(c->u.cpu.backup)) + backup_rate = clk_get_rate(c->u.cpu.backup); + if (c->u.cpu.backup->flags & PLL_FIXED) { + clk_set_rate(c->parent, rate); + if (rate <= backup_rate) + goto out; + } else if (rate == backup_rate) goto out; if (rate != clk_get_rate(c->u.cpu.main)) { @@ -3097,6 +3127,7 @@ static struct clk_mux_sel mux_sclk[] = { static struct clk tegra_clk_cclk_g = { .name = "cclk_g", + .flags = DIV_U71 | DIV_U71_INT, .inputs = mux_cclk_g, .reg = 0x368, .ops = &tegra_super_ops, @@ -3105,7 +3136,7 @@ static struct clk tegra_clk_cclk_g = { static struct clk tegra_clk_cclk_lp = { .name = "cclk_lp", - .flags = DIV_2, + .flags = DIV_2 | DIV_U71 | DIV_U71_INT, .inputs = mux_cclk_lp, .reg = 0x370, .ops = &tegra_super_ops, @@ -3626,20 +3657,21 @@ static struct cpufreq_frequency_table freq_table_300MHz[] = { }; static struct cpufreq_frequency_table freq_table_1p0GHz[] = { - { 0, 216000 }, - { 1, 312000 }, - { 2, 456000 }, - { 3, 608000 }, - { 4, 760000 }, - { 5, 816000 }, - { 6, 912000 }, - { 7, 1000000 }, - { 8, CPUFREQ_TABLE_END }, + { 0, 108000 }, + { 1, 216000 }, + { 2, 312000 }, + { 3, 456000 }, + { 4, 608000 }, + { 5, 760000 }, + { 6, 816000 }, + { 7, 912000 }, + { 8, 1000000 }, + { 9, CPUFREQ_TABLE_END }, }; static struct tegra_cpufreq_table_data cpufreq_tables[] = { { freq_table_300MHz, 0, 1 }, - { freq_table_1p0GHz, 2, 6 }, + { freq_table_1p0GHz, 2, 7 }, }; static void clip_cpu_rate_limits( |