ARM: tegra: clock: Add function to set SDMMC tap delay
authorColin Cross <ccross@android.com>
Tue, 28 Dec 2010 22:54:10 +0000 (14:54 -0800)
committerColin Cross <ccross@android.com>
Thu, 30 Dec 2010 00:11:51 +0000 (16:11 -0800)
The SDMMC controllers have extra bits in the clock source
register that adjust the delay between the clock and data
to compenstate for delays on the PCB.  The values need to
be set from the clock code so the clock can be locked
during the read-modify-write on the clock source register.

Change-Id: Id25b7cc01fa4ec48478b60aefdf5e59bb040fbf2
Signed-off-by: Colin Cross <ccross@android.com>
arch/arm/mach-tegra/clock.c
arch/arm/mach-tegra/clock.h
arch/arm/mach-tegra/include/mach/clk.h
arch/arm/mach-tegra/tegra2_clocks.c

index e3936af38356a64318173cee4971a390cdf48faa..f55697765cdfd9a48b35977c4a593561e13b84f6 100644 (file)
@@ -555,6 +555,17 @@ int __init tegra_late_init_clock(void)
 }
 late_initcall(tegra_late_init_clock);
 
+/* The SDMMC controllers have extra bits in the clock source register that
+ * adjust the delay between the clock and data to compenstate for delays
+ * on the PCB. */
+void tegra_sdmmc_tap_delay(struct clk *c, int delay) {
+       unsigned long flags;
+
+       clk_lock_save(c, flags);
+       tegra2_sdmmc_tap_delay(c, delay);
+       clk_unlock_restore(c, flags);
+}
+
 #ifdef CONFIG_DEBUG_FS
 
 /*
index 1d6a9acba412e9b0be2ef3a5cd6c3a0ee9185d16..f3319d30e29c9c0d63f67b309292e378d99f0958 100644 (file)
@@ -162,5 +162,6 @@ int clk_reparent(struct clk *c, struct clk *parent);
 void tegra_clk_init_from_table(struct tegra_clk_init_table *table);
 void clk_set_cansleep(struct clk *c);
 unsigned long clk_get_rate_locked(struct clk *c);
+void tegra2_sdmmc_tap_delay(struct clk *c, int delay);
 
 #endif
index f96f8c7c53ee6015c4e7439bb3d757d7780ce4be..d89463dcdf897bff77f0fd0c569f05362a263af6 100644 (file)
@@ -27,5 +27,6 @@ void tegra_periph_reset_assert(struct clk *c);
 
 int tegra_dvfs_set_rate(struct clk *c, unsigned long rate);
 unsigned long clk_get_rate_all_locked(struct clk *c);
+void tegra_sdmmc_tap_delay(struct clk *c, int delay);
 
 #endif
index b5494ae4c4a2e44226b6bacb7401276e13dc7d74..692bb845d588852766d7d0ed5d4dfa1e3d975649 100644 (file)
 #define PERIPH_CLK_SOURCE_DIVU16_MASK  0xFFFF
 #define PERIPH_CLK_SOURCE_DIV_SHIFT    0
 
+#define SDMMC_CLK_INT_FB_SEL           (1 << 23)
+#define SDMMC_CLK_INT_FB_DLY_SHIFT     16
+#define SDMMC_CLK_INT_FB_DLY_MASK      (0xF << SDMMC_CLK_INT_FB_DLY_SHIFT)
+
 #define PLL_BASE                       0x0
 #define PLL_BASE_BYPASS                        (1<<31)
 #define PLL_BASE_ENABLE                        (1<<30)
@@ -1022,6 +1026,20 @@ static struct clk_ops tegra_periph_clk_ops = {
        .reset                  = &tegra2_periph_clk_reset,
 };
 
+/* The SDMMC controllers have extra bits in the clock source register that
+ * adjust the delay between the clock and data to compenstate for delays
+ * on the PCB. */
+void tegra2_sdmmc_tap_delay(struct clk *c, int delay) {
+       u32 reg;
+
+       delay = clamp(delay, 0, 15);
+       reg = clk_readl(c->reg);
+       reg &= ~SDMMC_CLK_INT_FB_DLY_MASK;
+       reg |= SDMMC_CLK_INT_FB_SEL;
+       reg |= delay << SDMMC_CLK_INT_FB_DLY_SHIFT;
+       clk_writel(reg, c->reg);
+}
+
 /* External memory controller clock ops */
 static void tegra2_emc_clk_init(struct clk *c)
 {