[U-Boot] [PATCH v3 0/6] Tegra30: MMC: Add DT-based MMC driver for Tegra30/Cardhu

This patchset adds SDMMC device-tree support to the Tegra30 dts files, and enables the Tegra MMC driver on Tegra30 Cardhu.
I've tested this on my Cardhu-A04 and everything works fine, including card detect. All Tegra boards also build w/o error, and Seaboard MMC functionality is unchanged.
Changes in v2: - Use mmc_id instead of base address in pad_init_mmc - Remove all TEGRA_SDMMCx_BASE defines, no longer used - Add/use compatible entry for Tegra30 SDMMC driver Changes in v3: - Remove GP SDIO pad cfg writes - done in pinmux/padctrl init tables
Tom Warren (6): Tegra30: fdt: Add SDMMC (sdhci) nodes for T30 boards (Cardhu for now) Tegra: MMC: Added/update SDMMC registers/base addresses for T20/T30 Tegra30: MMC: Add SD bus power-rail and SDMMC pad init routines mmc: Tegra: Add SD bus power/voltage function and MMC pad init call. Tegra30: mmc: Add Tegra30 SDMMC compatible entry to fdtdec & driver Tegra30: MMC: Enable DT MMC driver support for Tegra30 Cardhu boards
arch/arm/dts/tegra30.dtsi | 32 ++++++++++++++ arch/arm/include/asm/arch-tegra/tegra_mmc.h | 35 ++++++++++++--- board/nvidia/cardhu/cardhu.c | 51 +++++++++++++++++++++ board/nvidia/common/board.c | 31 +++++++++++++- board/nvidia/dts/tegra30-cardhu.dts | 15 ++++++ drivers/mmc/tegra_mmc.c | 63 +++++++++++++++++++++++--- include/configs/cardhu.h | 20 ++++++++- include/configs/tegra30-common.h | 3 + include/fdtdec.h | 3 +- lib/fdtdec.c | 1 + 10 files changed, 237 insertions(+), 17 deletions(-)

Took these values directly from the kernel dts files.
Signed-off-by: Tom Warren twarren@nvidia.com --- v2: - no change v3: - no change
arch/arm/dts/tegra30.dtsi | 32 ++++++++++++++++++++++++++++++++ board/nvidia/dts/tegra30-cardhu.dts | 15 +++++++++++++++ 2 files changed, 47 insertions(+), 0 deletions(-)
diff --git a/arch/arm/dts/tegra30.dtsi b/arch/arm/dts/tegra30.dtsi index 9483e80..ccf154f 100644 --- a/arch/arm/dts/tegra30.dtsi +++ b/arch/arm/dts/tegra30.dtsi @@ -184,4 +184,36 @@ clocks = <&tegra_car 105>; status = "disabled"; }; + + sdhci@78000000 { + compatible = "nvidia,tegra30-sdhci"; + reg = <0x78000000 0x200>; + interrupts = <0 14 0x04>; + clocks = <&tegra_car 14>; + status = "disabled"; + }; + + sdhci@78000200 { + compatible = "nvidia,tegra30-sdhci"; + reg = <0x78000200 0x200>; + interrupts = <0 15 0x04>; + clocks = <&tegra_car 9>; + status = "disabled"; + }; + + sdhci@78000400 { + compatible = "nvidia,tegra30-sdhci"; + reg = <0x78000400 0x200>; + interrupts = <0 19 0x04>; + clocks = <&tegra_car 69>; + status = "disabled"; + }; + + sdhci@78000600 { + compatible = "nvidia,tegra30-sdhci"; + reg = <0x78000600 0x200>; + interrupts = <0 31 0x04>; + clocks = <&tegra_car 15>; + status = "disabled"; + }; }; diff --git a/board/nvidia/dts/tegra30-cardhu.dts b/board/nvidia/dts/tegra30-cardhu.dts index 48039c9..4d22b48 100644 --- a/board/nvidia/dts/tegra30-cardhu.dts +++ b/board/nvidia/dts/tegra30-cardhu.dts @@ -12,6 +12,8 @@ i2c2 = "/i2c@7000c400"; i2c3 = "/i2c@7000c500"; i2c4 = "/i2c@7000c700"; + sdhci0 = "/sdhci@78000600"; + sdhci1 = "/sdhci@78000000"; };
memory { @@ -48,4 +50,17 @@ status = "okay"; spi-max-frequency = <25000000>; }; + + sdhci@78000000 { + status = "okay"; + cd-gpios = <&gpio 69 1>; /* gpio PI5 */ + wp-gpios = <&gpio 155 0>; /* gpio PT3 */ + power-gpios = <&gpio 31 0>; /* gpio PD7 */ + bus-width = <4>; + }; + + sdhci@78000600 { + status = "okay"; + bus-width = <8>; + }; };

Removed SDMMC base addresses from tegra.h since they're no longer used. Added additional vendor-specific SD/MMC registers and bus power defines.
Signed-off-by: Tom Warren twarren@nvidia.com --- v2: - change pad_init_mmc prototype - remove all TEGRA_SDMMCx_BASE defines v3: - remove GP padctrl SDIOCFG defines, done in previous padctrl patch
arch/arm/include/asm/arch-tegra/tegra_mmc.h | 35 ++++++++++++++++++++++---- 1 files changed, 29 insertions(+), 6 deletions(-)
diff --git a/arch/arm/include/asm/arch-tegra/tegra_mmc.h b/arch/arm/include/asm/arch-tegra/tegra_mmc.h index bd18f5f..2a3f830 100644 --- a/arch/arm/include/asm/arch-tegra/tegra_mmc.h +++ b/arch/arm/include/asm/arch-tegra/tegra_mmc.h @@ -22,10 +22,7 @@ #ifndef __TEGRA_MMC_H_ #define __TEGRA_MMC_H_
-#define TEGRA_SDMMC1_BASE 0xC8000000 -#define TEGRA_SDMMC2_BASE 0xC8000200 -#define TEGRA_SDMMC3_BASE 0xC8000400 -#define TEGRA_SDMMC4_BASE 0xC8000600 +#include <fdtdec.h>
#define MAX_HOSTS 4 /* Max number of 'hosts'/controllers */
@@ -64,12 +61,30 @@ struct tegra_mmc { unsigned char admaerr; /* offset 54h */ unsigned char res4[3]; /* RESERVED, offset 55h-57h */ unsigned long admaaddr; /* offset 58h-5Fh */ - unsigned char res5[0x9c]; /* RESERVED, offset 60h-FBh */ + unsigned char res5[0xa0]; /* RESERVED, offset 60h-FBh */ unsigned short slotintstatus; /* offset FCh */ unsigned short hcver; /* HOST Version */ - unsigned char res6[0x100]; /* RESERVED, offset 100h-1FFh */ + unsigned int venclkctl; /* _VENDOR_CLOCK_CNTRL_0, 100h */ + unsigned int venspictl; /* _VENDOR_SPI_CNTRL_0, 104h */ + unsigned int venspiintsts; /* _VENDOR_SPI_INT_STATUS_0, 108h */ + unsigned int venceatactl; /* _VENDOR_CEATA_CNTRL_0, 10Ch */ + unsigned int venbootctl; /* _VENDOR_BOOT_CNTRL_0, 110h */ + unsigned int venbootacktout; /* _VENDOR_BOOT_ACK_TIMEOUT, 114h */ + unsigned int venbootdattout; /* _VENDOR_BOOT_DAT_TIMEOUT, 118h */ + unsigned int vendebouncecnt; /* _VENDOR_DEBOUNCE_COUNT_0, 11Ch */ + unsigned int venmiscctl; /* _VENDOR_MISC_CNTRL_0, 120h */ + unsigned int res6[47]; /* 0x124 ~ 0x1DC */ + unsigned int sdmemcmppadctl; /* _SDMEMCOMPPADCTRL_0, 1E0h */ + unsigned int autocalcfg; /* _AUTO_CAL_CONFIG_0, 1E4h */ + unsigned int autocalintval; /* _AUTO_CAL_INTERVAL_0, 1E8h */ + unsigned int autocalsts; /* _AUTO_CAL_STATUS_0, 1ECh */ };
+#define TEGRA_MMC_PWRCTL_SD_BUS_POWER (1 << 0) +#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8 (5 << 1) +#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_0 (6 << 1) +#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3 (7 << 1) + #define TEGRA_MMC_HOSTCTL_DMASEL_MASK (3 << 3) #define TEGRA_MMC_HOSTCTL_DMASEL_SDMA (0 << 3) #define TEGRA_MMC_HOSTCTL_DMASEL_ADMA2_32BIT (2 << 3) @@ -119,6 +134,12 @@ struct tegra_mmc {
#define TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE (1 << 1)
+/* SDMMC1/3 settings from section 24.6 of T30 TRM */ +#define MEMCOMP_PADCTRL_VREF 7 +#define AUTO_CAL_ENABLED (1 << 29) +#define AUTO_CAL_PD_OFFSET (0x70 << 8) +#define AUTO_CAL_PU_OFFSET (0x62 << 0) + struct mmc_host { struct tegra_mmc *reg; int id; /* device id/number, 0-3 */ @@ -132,5 +153,7 @@ struct mmc_host { unsigned int clock; /* Current clock (MHz) */ };
+void pad_init_mmc(struct mmc_host *host); + #endif /* __ASSEMBLY__ */ #endif /* __TEGRA_MMC_H_ */

T30 requires specific SDMMC pad programming, and bus power-rail bringup.
Signed-off-by: Tom Warren twarren@nvidia.com --- v2: - rewrite pad_init_mmc to use mmc_id instead of SDMMC base address - add PMU-specific comments to board_sdmmc_voltage_init sequence v3: - removed GP_SDIOCFG writes, now done during pinmux init via table
board/nvidia/cardhu/cardhu.c | 51 ++++++++++++++++++++++++++++++++++++++++++ board/nvidia/common/board.c | 31 ++++++++++++++++++++++++- 2 files changed, 81 insertions(+), 1 deletions(-)
diff --git a/board/nvidia/cardhu/cardhu.c b/board/nvidia/cardhu/cardhu.c index 08e9b7b..3544b41 100644 --- a/board/nvidia/cardhu/cardhu.c +++ b/board/nvidia/cardhu/cardhu.c @@ -25,6 +25,10 @@ #include <asm/arch/pinmux.h> #include <asm/arch/gp_padctrl.h> #include "pinmux-config-cardhu.h" +#include <i2c.h> + +#define PMU_I2C_ADDRESS 0x2D +#define MAX_I2C_RETRY 3
/* * Routine: pinmux_init @@ -41,3 +45,50 @@ void pinmux_init(void) /* Initialize any non-default pad configs (APB_MISC_GP regs) */ padgrp_config_table(cardhu_padctrl, ARRAY_SIZE(cardhu_padctrl)); } + +#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 */ + + /* TPS659110: LDO5_REG = 3.3v, ACTIVE to SDMMC1 */ + 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); + } + + /* TPS659110: GPIO7_REG = PDEN, output a 1 to EN_3V3_SYS */ + 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..7d9f361 100644 --- a/board/nvidia/common/board.c +++ b/board/nvidia/common/board.c @@ -49,6 +49,7 @@ #include <asm/arch-tegra/usb.h> #endif #ifdef CONFIG_TEGRA_MMC +#include <asm/arch-tegra/tegra_mmc.h> #include <asm/arch-tegra/mmc.h> #endif #include <i2c.h> @@ -245,4 +246,32 @@ int board_mmc_init(bd_t *bd)
return 0; } -#endif + +void pad_init_mmc(struct mmc_host *host) +{ +#if defined(CONFIG_TEGRA30) + enum periph_id id = host->mmc_id; + u32 val; + + debug("%s: sdmmc address = %08x, id = %d\n", __func__, + (unsigned int)host->reg, id); + + /* Set the pad drive strength for SDMMC1 or 3 only */ + if (id != PERIPH_ID_SDMMC1 && id != PERIPH_ID_SDMMC3) { + debug("%s: settings are only valid for SDMMC1/SDMMC3!\n", + __func__); + return; + } + + val = readl(&host->reg->sdmemcmppadctl); + val &= 0xFFFFFFF0; + val |= MEMCOMP_PADCTRL_VREF; + writel(val, &host->reg->sdmemcmppadctl); + + val = readl(&host->reg->autocalcfg); + val &= 0xFFFF0000; + val |= AUTO_CAL_PU_OFFSET | AUTO_CAL_PD_OFFSET | AUTO_CAL_ENABLED; + writel(val, &host->reg->autocalcfg); +#endif /* T30 */ +} +#endif /* MMC */

Tegra30 requires the SD Bus Voltage & Power bits be set in the SD Power Control register. Tegra20 works w/o them set, but do it anyway for those SoCs as it's part of the SD spec. Also call a common board pad init routine (pad_init_mmc) in mmc_reset(), used by Tegra30 only for now.
Note that Tegra20 SD/MMC HW differs enough from Tegra20 that a new compatible entry is used in the fdt compat_names/id tables.
Signed-off-by: Tom Warren twarren@nvidia.com --- v2: - pass mmc 'host' struct to pad_init_mmc - probe for Tegra30 SDMMC id in tegra_mmc_init V3: - no change
drivers/mmc/tegra_mmc.c | 63 +++++++++++++++++++++++++++++++++++++++++------ 1 files changed, 55 insertions(+), 8 deletions(-)
diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c index 6063d08..8f5f1a5 100644 --- a/drivers/mmc/tegra_mmc.c +++ b/drivers/mmc/tegra_mmc.c @@ -21,7 +21,6 @@
#include <bouncebuf.h> #include <common.h> -#include <fdtdec.h> #include <asm/gpio.h> #include <asm/io.h> #include <asm/arch/clock.h> @@ -38,6 +37,38 @@ struct mmc_host mmc_host[MAX_HOSTS]; #error "Please enable device tree support to use this driver" #endif
+static void mmc_set_power(struct mmc_host *host, unsigned short power) +{ + u8 pwr = 0; + debug("%s: power = %x\n", __func__, power); + + if (power != (unsigned short)-1) { + switch (1 << power) { + case MMC_VDD_165_195: + pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8; + break; + case MMC_VDD_29_30: + case MMC_VDD_30_31: + pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_0; + break; + case MMC_VDD_32_33: + case MMC_VDD_33_34: + pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3; + break; + } + } + debug("%s: pwr = %X\n", __func__, pwr); + + /* Set the bus voltage first (if any) */ + writeb(pwr, &host->reg->pwrcon); + if (pwr == 0) + return; + + /* Now enable bus power */ + pwr |= TEGRA_MMC_PWRCTL_SD_BUS_POWER; + writeb(pwr, &host->reg->pwrcon); +} + static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data, struct bounce_buffer *bbstate) { @@ -334,8 +365,7 @@ static void mmc_change_clock(struct mmc_host *host, uint clock) debug(" mmc_change_clock called\n");
/* - * Change Tegra SDMMCx clock divisor here. Source is 216MHz, - * PLLP_OUT0 + * Change Tegra SDMMCx clock divisor here. Source is PLLP_OUT0 */ if (clock == 0) goto out; @@ -410,7 +440,7 @@ static void mmc_set_ios(struct mmc *mmc) debug("mmc_set_ios: hostctl = %08X\n", ctrl); }
-static void mmc_reset(struct mmc_host *host) +static void mmc_reset(struct mmc_host *host, struct mmc *mmc) { unsigned int timeout; debug(" mmc_reset called\n"); @@ -436,6 +466,14 @@ static void mmc_reset(struct mmc_host *host) timeout--; udelay(1000); } + + /* Set SD bus voltage & enable bus power */ + mmc_set_power(host, fls(mmc->voltages) - 1); + debug("%s: power control = %02X, host control = %02X\n", __func__, + readb(&host->reg->pwrcon), readb(&host->reg->hostctl)); + + /* Make sure SDIO pads are set up */ + pad_init_mmc(host); }
static int mmc_core_init(struct mmc *mmc) @@ -444,7 +482,7 @@ static int mmc_core_init(struct mmc *mmc) unsigned int mask; debug(" mmc_core_init called\n");
- mmc_reset(host); + mmc_reset(host, mmc);
host->version = readw(&host->reg->hcver); debug("host version = %x\n", host->version); @@ -641,12 +679,21 @@ void tegra_mmc_init(void) const void *blob = gd->fdt_blob; debug("%s entry\n", __func__);
+ /* See if any Tegra30 MMC controllers are present */ count = fdtdec_find_aliases_for_id(blob, "sdhci", - COMPAT_NVIDIA_TEGRA20_SDMMC, node_list, MAX_HOSTS); - debug("%s: count of sdhci nodes is %d\n", __func__, count); + COMPAT_NVIDIA_TEGRA30_SDMMC, node_list, MAX_HOSTS); + debug("%s: count of T30 sdhci nodes is %d\n", __func__, count); + if (process_nodes(blob, node_list, count)) { + printf("%s: Error processing T30 mmc node(s)!\n", __func__); + return; + }
+ /* Now look for any Tegra20 MMC controllers */ + count = fdtdec_find_aliases_for_id(blob, "sdhci", + COMPAT_NVIDIA_TEGRA20_SDMMC, node_list, MAX_HOSTS); + debug("%s: count of T20 sdhci nodes is %d\n", __func__, count); if (process_nodes(blob, node_list, count)) { - printf("%s: Error processing mmc node(s)!\n", __func__); + printf("%s: Error processing T20 mmc node(s)!\n", __func__); return; } }

Tegra30 SD/MMC controller differs enough from Tegra20 that it needs its own entry in the compat_names/compat_id tables and in the Tegra MMC driver.
Signed-off-by: Tom Warren twarren@nvidia.com --- v2: new v3: - no change
include/fdtdec.h | 3 ++- lib/fdtdec.c | 1 + 2 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/include/fdtdec.h b/include/fdtdec.h index 32730d2..2189483 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -71,7 +71,8 @@ enum fdt_compat_id { COMPAT_NVIDIA_TEGRA20_NAND, /* Tegra2 NAND controller */ COMPAT_NVIDIA_TEGRA20_PWM, /* Tegra 2 PWM controller */ COMPAT_NVIDIA_TEGRA20_DC, /* Tegra 2 Display controller */ - COMPAT_NVIDIA_TEGRA20_SDMMC, /* Tegra SDMMC controller */ + COMPAT_NVIDIA_TEGRA30_SDMMC, /* Tegra30 SDMMC controller */ + COMPAT_NVIDIA_TEGRA20_SDMMC, /* Tegra20 SDMMC controller */ COMPAT_NVIDIA_TEGRA20_SFLASH, /* Tegra 2 SPI flash controller */ COMPAT_NVIDIA_TEGRA20_SLINK, /* Tegra 2 SPI SLINK controller */ COMPAT_SMSC_LAN9215, /* SMSC 10/100 Ethernet LAN9215 */ diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 000130f..43f29f5 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -46,6 +46,7 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(NVIDIA_TEGRA20_NAND, "nvidia,tegra20-nand"), COMPAT(NVIDIA_TEGRA20_PWM, "nvidia,tegra20-pwm"), COMPAT(NVIDIA_TEGRA20_DC, "nvidia,tegra20-dc"), + COMPAT(NVIDIA_TEGRA30_SDMMC, "nvidia,tegra30-sdhci"), COMPAT(NVIDIA_TEGRA20_SDMMC, "nvidia,tegra20-sdhci"), COMPAT(NVIDIA_TEGRA20_SFLASH, "nvidia,tegra20-sflash"), COMPAT(NVIDIA_TEGRA20_SLINK, "nvidia,tegra20-slink"),

Tested on my Cardhu-A04 tablet, eMMC and SD-Card work fine, can load a kernel off of an SD card OK, card detect works, and the env is now stored in eMMC (end of the 2nd 'boot' sector, same as Tegra20).
Signed-off-by: Tom Warren twarren@nvidia.com --- v2: - no change v3: - no change
include/configs/cardhu.h | 20 +++++++++++++++++++- include/configs/tegra30-common.h | 3 +++ 2 files changed, 22 insertions(+), 1 deletions(-)
diff --git a/include/configs/cardhu.h b/include/configs/cardhu.h index 1616b39..18c7eb8 100644 --- a/include/configs/cardhu.h +++ b/include/configs/cardhu.h @@ -47,7 +47,25 @@ #define CONFIG_SYS_I2C_SPEED 100000 #define CONFIG_CMD_I2C
-#define CONFIG_ENV_IS_NOWHERE +/* SD/MMC */ +#define CONFIG_MMC +#define CONFIG_GENERIC_MMC +#define CONFIG_TEGRA_MMC +#define CONFIG_CMD_MMC + +#define CONFIG_DOS_PARTITION +#define CONFIG_EFI_PARTITION +#define CONFIG_FS_EXT4 +#define CONFIG_FS_FAT +#define CONFIG_CMD_EXT2 +#define CONFIG_CMD_FAT +#define CONFIG_CMD_FS_GENERIC + +/* Environment in eMMC, at the end of 2nd "boot sector" */ +#define CONFIG_ENV_IS_IN_MMC +#define CONFIG_ENV_OFFSET ((512 * 1024) - CONFIG_ENV_SIZE) +#define CONFIG_SYS_MMC_ENV_DEV 0 +#define CONFIG_SYS_MMC_ENV_PART 2
/* SPI */ #define CONFIG_TEGRA_SLINK diff --git a/include/configs/tegra30-common.h b/include/configs/tegra30-common.h index 04517e1..bd1dfe8 100644 --- a/include/configs/tegra30-common.h +++ b/include/configs/tegra30-common.h @@ -86,4 +86,7 @@ /* Total I2C ports on Tegra30 */ #define TEGRA_I2C_NUM_CONTROLLERS 5
+/* Misc utility code */ +#define CONFIG_BOUNCE_BUFFER + #endif /* _TEGRA30_COMMON_H_ */

On 03/07/2013 02:47 PM, Tom Warren wrote:
Tested on my Cardhu-A04 tablet, eMMC and SD-Card work fine, can load a kernel off of an SD card OK, card detect works, and the env is now stored in eMMC (end of the 2nd 'boot' sector, same as Tegra20).
The series, Reviewed-by: Stephen Warren swarren@nvidia.com
participants (2)
-
Stephen Warren
-
Tom Warren