
T30 requires specific SDMMC pad programming, and bus power-rail bringup.
Signed-off-by: Tom Warren twarren@nvidia.com --- board/nvidia/cardhu/cardhu.c | 49 +++++++++++++++++++++++++++++++++++++++++ board/nvidia/common/board.c | 50 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 1 deletions(-)
diff --git a/board/nvidia/cardhu/cardhu.c b/board/nvidia/cardhu/cardhu.c index df4cb6b..363bddc 100644 --- a/board/nvidia/cardhu/cardhu.c +++ b/board/nvidia/cardhu/cardhu.c @@ -24,6 +24,10 @@ #include <common.h> #include <asm/arch/pinmux.h> #include "pinmux-config-cardhu.h" +#include <i2c.h> + +#define PMU_I2C_ADDRESS 0x2D +#define MAX_I2C_RETRY 3
/* * Routine: pinmux_init @@ -37,3 +41,48 @@ void pinmux_init(void) pinmux_config_table(unused_pins_lowpower, ARRAY_SIZE(unused_pins_lowpower)); } + +#if defined(CONFIG_TEGRA_MMC) +/* + * Do I2C/PMU writes to bring up SD card bus power + * + */ +void board_sdmmc_voltage_init(void) +{ + uchar reg, data_buffer[1]; + int i; + + i2c_set_bus_num(0); /* PMU is on bus 0 */ + + data_buffer[0] = 0x65; + reg = 0x32; + + for (i = 0; i < MAX_I2C_RETRY; ++i) { + if (i2c_write(PMU_I2C_ADDRESS, reg, 1, data_buffer, 1)) + udelay(100); + } + + data_buffer[0] = 0x09; + reg = 0x67; + + for (i = 0; i < MAX_I2C_RETRY; ++i) { + if (i2c_write(PMU_I2C_ADDRESS, reg, 1, data_buffer, 1)) + udelay(100); + } +} + +/* + * Routine: pin_mux_mmc + * Description: setup the MMC muxes, power rails, etc. + */ +void pin_mux_mmc(void) +{ + /* + * NOTE: We don't do mmc-specific pin muxes here. + * They were done globally in pinmux_init(). + */ + + /* Bring up the SDIO1 power rail */ + board_sdmmc_voltage_init(); +} +#endif /* MMC */ diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c index babbe08..4ca6b29 100644 --- a/board/nvidia/common/board.c +++ b/board/nvidia/common/board.c @@ -49,6 +49,8 @@ #include <asm/arch-tegra/usb.h> #endif #ifdef CONFIG_TEGRA_MMC +#include <asm/arch/gp_padctrl.h> +#include <asm/arch-tegra/tegra_mmc.h> #include <asm/arch-tegra/mmc.h> #endif #include <i2c.h> @@ -245,4 +247,50 @@ int board_mmc_init(bd_t *bd)
return 0; } -#endif + +void pad_init_mmc(struct tegra_mmc *reg) +{ +#if defined(CONFIG_TEGRA30) + struct apb_misc_gp_ctlr *const gpc = + (struct apb_misc_gp_ctlr *)NV_PA_APB_MISC_GP_BASE; + struct tegra_mmc *const sdmmc = (struct tegra_mmc *)reg; + u32 val, padcfg, padmask, offset = (unsigned int)reg; + + debug("%s: sdmmc address = %08x\n", __func__, (unsigned int)sdmmc); + + /* Set the pad drive strength for SDMMC1 or 3 only */ + if (offset != TEGRA_SDMMC1_BASE && offset != TEGRA_SDMMC3_BASE) { + debug("%s: settings are only valid for SDMMC1/SDMMC3!\n", + __func__); + return; + } + + /* Set pads as per T30 TRM, section 24.6.1.2 */ + padcfg = (GP_SDIOCFG_DRVUP_SLWF | GP_SDIOCFG_DRVDN_SLWR | \ + GP_SDIOCFG_DRVUP | GP_SDIOCFG_DRVDN); + padmask = 0x00000FFF; + + if (offset == TEGRA_SDMMC1_BASE) { + val = readl(&gpc->sdio1cfg); + val &= padmask; + val |= padcfg; + writel(val, &gpc->sdio1cfg); + } else {/* SDMMC3 */ + val = readl(&gpc->sdio3cfg); + val &= padmask; + val |= padcfg; + writel(val, &gpc->sdio3cfg); + } + + val = readl(&sdmmc->sdmemcmppadctl); + val &= 0xFFFFFFF0; + val |= MEMCOMP_PADCTRL_VREF; + writel(val, &sdmmc->sdmemcmppadctl); + + val = readl(&sdmmc->autocalcfg); + val &= 0xFFFF0000; + val |= AUTO_CAL_PU_OFFSET | AUTO_CAL_PD_OFFSET | AUTO_CAL_ENABLED; + writel(val, &sdmmc->autocalcfg); +#endif /* T30 */ +} +#endif /* MMC */