diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c index e3936af38356..f55697765cdf 100644 --- a/arch/arm/mach-tegra/clock.c +++ b/arch/arm/mach-tegra/clock.c @@ -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 /* diff --git a/arch/arm/mach-tegra/clock.h b/arch/arm/mach-tegra/clock.h index 1d6a9acba412..f3319d30e29c 100644 --- a/arch/arm/mach-tegra/clock.h +++ b/arch/arm/mach-tegra/clock.h @@ -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 diff --git a/arch/arm/mach-tegra/include/mach/clk.h b/arch/arm/mach-tegra/include/mach/clk.h index f96f8c7c53ee..d89463dcdf89 100644 --- a/arch/arm/mach-tegra/include/mach/clk.h +++ b/arch/arm/mach-tegra/include/mach/clk.h @@ -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 diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c index b5494ae4c4a2..692bb845d588 100644 --- a/arch/arm/mach-tegra/tegra2_clocks.c +++ b/arch/arm/mach-tegra/tegra2_clocks.c @@ -75,6 +75,10 @@ #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) {