summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2011-05-03 00:51:06 -0700
committerVarun Colbert <vcolbert@nvidia.com>2011-05-13 18:42:18 -0700
commitf1e0b9de06c999a8129aa2d129402d0d19be6fa3 (patch)
tree010323115b5888908759e1e4f42b72d7ceeb54a7
parent2803bf02c3cba4bd117e5a51db336de513f1174c (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.c76
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(