ARM: tegra: clocks: Add emc scaling
authorColin Cross <ccross@android.com>
Tue, 23 Nov 2010 02:37:54 +0000 (18:37 -0800)
committerColin Cross <ccross@android.com>
Mon, 21 Feb 2011 08:16:45 +0000 (00:16 -0800)
Add clock ops on the emc peripheral clock that call into the
emc driver to update the memory controller registers for the
new frequency.  Tegra has an interlock between the clock
controller and the memory controller that prevents the new
register values from taking effect until the clock frequency
update occurs.

Acked-by: Olof Johansson <olof@lixom.net>
Signed-off-by: Colin Cross <ccross@android.com>
arch/arm/mach-tegra/tegra2_clocks.c

index f6f685c4f3397ab346af14d18e1514b1bc0c90b2..7f70687140492f6d4773ddc25e0f22d4699d3a70 100644 (file)
@@ -31,6 +31,7 @@
 
 #include "clock.h"
 #include "fuse.h"
+#include "tegra2_emc.h"
 
 #define RST_DEVICES                    0x004
 #define RST_DEVICES_SET                        0x300
@@ -1051,6 +1052,55 @@ static struct clk_ops tegra_periph_clk_ops = {
        .reset                  = &tegra2_periph_clk_reset,
 };
 
+/* External memory controller clock ops */
+static void tegra2_emc_clk_init(struct clk *c)
+{
+       tegra2_periph_clk_init(c);
+       c->max_rate = clk_get_rate_locked(c);
+}
+
+static long tegra2_emc_clk_round_rate(struct clk *c, unsigned long rate)
+{
+       long new_rate = rate;
+
+       new_rate = tegra_emc_round_rate(new_rate);
+       if (new_rate < 0)
+               return c->max_rate;
+
+       BUG_ON(new_rate != tegra2_periph_clk_round_rate(c, new_rate));
+
+       return new_rate;
+}
+
+static int tegra2_emc_clk_set_rate(struct clk *c, unsigned long rate)
+{
+       int ret;
+       /*
+        * The Tegra2 memory controller has an interlock with the clock
+        * block that allows memory shadowed registers to be updated,
+        * and then transfer them to the main registers at the same
+        * time as the clock update without glitches.
+        */
+       ret = tegra_emc_set_rate(rate);
+       if (ret < 0)
+               return ret;
+
+       ret = tegra2_periph_clk_set_rate(c, rate);
+       udelay(1);
+
+       return ret;
+}
+
+static struct clk_ops tegra_emc_clk_ops = {
+       .init                   = &tegra2_emc_clk_init,
+       .enable                 = &tegra2_periph_clk_enable,
+       .disable                = &tegra2_periph_clk_disable,
+       .set_parent             = &tegra2_periph_clk_set_parent,
+       .set_rate               = &tegra2_emc_clk_set_rate,
+       .round_rate             = &tegra2_emc_clk_round_rate,
+       .reset                  = &tegra2_periph_clk_reset,
+};
+
 /* Clock doubler ops */
 static void tegra2_clk_double_init(struct clk *c)
 {
@@ -1948,6 +1998,18 @@ static struct clk_mux_sel mux_pclk[] = {
        { 0, 0},
 };
 
+static struct clk tegra_clk_emc = {
+       .name = "emc",
+       .ops = &tegra_emc_clk_ops,
+       .reg = 0x19c,
+       .max_rate = 800000000,
+       .inputs = mux_pllm_pllc_pllp_clkm,
+       .flags = MUX | DIV_U71 | PERIPH_EMC_ENB,
+       .u.periph = {
+               .clk_num = 57,
+       },
+};
+
 #define PERIPH_CLK(_name, _dev, _con, _clk_num, _reg, _max, _inputs, _flags) \
        {                                               \
                .name      = _name,                     \
@@ -2039,7 +2101,6 @@ struct clk tegra_list_clks[] = {
        PERIPH_CLK("usbd",      "fsl-tegra-udc",        NULL,   22,     0,      480000000, mux_clk_m,                   0), /* requires min voltage */
        PERIPH_CLK("usb2",      "tegra-ehci.1",         NULL,   58,     0,      480000000, mux_clk_m,                   0), /* requires min voltage */
        PERIPH_CLK("usb3",      "tegra-ehci.2",         NULL,   59,     0,      480000000, mux_clk_m,                   0), /* requires min voltage */
-       PERIPH_CLK("emc",       "emc",                  NULL,   57,     0x19c,  800000000, mux_pllm_pllc_pllp_clkm,     MUX | DIV_U71 | PERIPH_EMC_ENB),
        PERIPH_CLK("dsi",       "dsi",                  NULL,   48,     0,      500000000, mux_plld,                    0), /* scales with voltage */
        PERIPH_CLK("csi",       "tegra_camera",         "csi",  52,     0,      72000000,  mux_pllp_out3,               0),
        PERIPH_CLK("isp",       "tegra_camera",         "isp",  23,     0,      150000000, mux_clk_m,                   0), /* same frequency as VI */
@@ -2115,6 +2176,7 @@ struct clk *tegra_ptr_clks[] = {
        &tegra_dev2_clk,
        &tegra_clk_virtual_cpu,
        &tegra_clk_blink,
+       &tegra_clk_emc,
 };
 
 static void tegra2_init_one_clock(struct clk *c)