[U-Boot] [PATCH 00/23] mmc: omap5: Add support for UHS and HS200 modes

This series adds the missing bit to enable the UHS, HS200 and DDR52 modes on the omap5 platforms (DRA7 and AM57).
It relies on the following series: * [PATCH v2 00/26] mmc: Add support for HS200 and UHS modes * [PATCH v2 0/5] mmc: omap_hsmmc: Add support for ADMA * [PATCH v3 0/2] power: Add a driver to handle the PBIAS cell of the TI SOCs
Enabling support for high speed modes on omap5 requires implementing: * io signal voltage selection * tuning support * pin configuration (IO delays)
The few last patches enable the high mode for all omap5 platforms except some for which we need to disable either the UHS or the HS200 because the volatge regulators on board would not allow using those modes. This is not a SOC limitation.
With this in place we observe significant improvements in the performances: on a DRA72 evm: eMMC HS200: 124 MB/s eMMC DDR52: 78 MB/s sd SDR104: 71 MB/s sd SDR50: 44 MB/s For the record, the original performances were: SD High speed: 18 MB/s MMC High speed: 18 MB/s
This series has been tested on: * DRA71-evm * DRA72-evm * DRA7x-evm * DRA76-evm * AM57x-evm * Beaglebone Black * AM335x evm * AM437x SK
Jean-Jacques
Jean-Jacques Hiblot (8): mmc: omap_hsmmc: Reduce the max timeout for reset controller fsm mmc: omap_hsmmc: allow the simple HS modes to use the default pinctrl mmc: omap_hsmmc: update mmc->clock with the actual bus speed mmc: omap_hsmmc: implement send_init_stream callback mmc: omap_hsmmc: add signal voltage selection support ARM: dts: dra7: Add supported MMC/SD modes in MMC dt nodes dts: am57xx-beagle-x15: disable UHS and HS200 support dts: am57xx-idk: disable HS200 support
Kishon Vijay Abraham I (15): mmc: omap_hsmmc: cleanup clock configuration mmc: omap_hsmmc: cleanup omap_hsmmc_set_ios mmc: omap_hsmmc: add support to set default io voltage mmc: omap_hsmmc: set MMC mode in the UHSMS bit field mmc: omap_hsmmc: Enable DDR mode support mmc: omap_hsmmc: Add tuning support mmc: omap_hsmmc: Workaround for errata id i802 mmc: omap_hsmmc: use mmc_of_parse to populate mmc_config ARM: OMAP5/DRA7: Enable iodelay recalibration to be done from uboot mmc: omap_hsmmc: Add support to set IODELAY values mmc: omap_hsmmc: Add support to get pinctrl values and max frequency for different hw revisions mmc: omap_hsmmc: allow mmc clock to be gated ARM: OMAP5: set mmc clock frequency to 192MHz ARM: dts: DRA7: use new dra7-specific compatible string ARM: DRA7x/AM57x: Add MMC/SD fixups for rev1.0 and rev 1.1
arch/arm/dts/am57xx-beagle-x15.dts | 6 + arch/arm/dts/am57xx-idk-common.dtsi | 2 + arch/arm/dts/dra7.dtsi | 22 +- arch/arm/include/asm/arch-omap5/clock.h | 2 +- arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h | 3 + arch/arm/include/asm/arch-omap5/sys_proto.h | 7 + arch/arm/include/asm/omap_mmc.h | 59 +- arch/arm/mach-omap2/omap5/dra7xx_iodelay.c | 30 + arch/arm/mach-omap2/omap5/hw_data.c | 10 +- board/ti/am57xx/board.c | 30 + board/ti/dra7xx/evm.c | 29 + drivers/mmc/omap_hsmmc.c | 940 +++++++++++++++++++++-- include/configs/am57xx_evm.h | 2 - include/configs/dra7xx_evm.h | 2 - 14 files changed, 1071 insertions(+), 73 deletions(-)

From: Kishon Vijay Abraham I kishon@ti.com
Add a separate function for starting the clock, stopping the clock and setting the clock. Starting the clock and stopping the clock can be used irrespective of setting the clock (For example during iodelay recalibration). Also set the clock only if there is a change in frequency.
Signed-off-by: Kishon Vijay Abraham I kishon@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- arch/arm/include/asm/omap_mmc.h | 2 ++ drivers/mmc/omap_hsmmc.c | 74 ++++++++++++++++++++++++++++------------- 2 files changed, 52 insertions(+), 24 deletions(-)
diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index bf9de9b..102aec2 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -172,6 +172,8 @@ struct omap_hsmmc_plat { #define CLK_400KHZ 1 #define CLK_MISC 2
+#define CLKD_MAX 0x3FF /* max clock divisor: 1023 */ + #define RSP_TYPE_NONE (RSP_TYPE_NORSP | CCCE_NOCHECK | CICE_NOCHECK) #define MMC_CMD0 (INDEX(0) | RSP_TYPE_NONE | DP_NO_DATA | DDIR_WRITE)
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 1c3d1a5..6ef8295 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -62,6 +62,7 @@ struct omap_hsmmc_data { #if !CONFIG_IS_ENABLED(DM_MMC) struct mmc_config cfg; #endif + uint clock; #ifdef OMAP_HSMMC_USE_GPIO #if CONFIG_IS_ENABLED(DM_MMC) struct gpio_desc cd_gpio; /* Change Detect GPIO */ @@ -114,6 +115,8 @@ struct omap_hsmmc_adma_desc { static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size); static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, unsigned int siz); +static void omap_hsmmc_start_clock(struct hsmmc *mmc_base); +static void omap_hsmmc_stop_clock(struct hsmmc *mmc_base);
static inline struct omap_hsmmc_data *omap_hsmmc_get_data(struct mmc *mmc) { @@ -762,6 +765,51 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, return 0; }
+static void omap_hsmmc_stop_clock(struct hsmmc *mmc_base) +{ + writel(readl(&mmc_base->sysctl) & ~CEN_ENABLE, &mmc_base->sysctl); +} + +static void omap_hsmmc_start_clock(struct hsmmc *mmc_base) +{ + writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl); +} + +static void omap_hsmmc_set_clock(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + struct hsmmc *mmc_base; + unsigned int dsor = 0; + ulong start; + + mmc_base = priv->base_addr; + omap_hsmmc_stop_clock(mmc_base); + + /* TODO: Is setting DTO required here? */ + mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK), + (ICE_STOP | DTO_15THDTO)); + + if (mmc->clock != 0) { + dsor = DIV_ROUND_UP(MMC_CLOCK_REFERENCE * 1000000, mmc->clock); + if (dsor > CLKD_MAX) + dsor = CLKD_MAX; + } + + mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK, + (dsor << CLKD_OFFSET) | ICE_OSCILLATE); + + start = get_timer(0); + while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY) { + if (get_timer(0) - start > MAX_RETRY_MS) { + printf("%s: timedout waiting for ics!\n", __func__); + return; + } + } + + priv->clock = mmc->clock; + omap_hsmmc_start_clock(mmc_base); +} + #if !CONFIG_IS_ENABLED(DM_MMC) static int omap_hsmmc_set_ios(struct mmc *mmc) { @@ -774,8 +822,6 @@ static int omap_hsmmc_set_ios(struct udevice *dev) struct mmc *mmc = upriv->mmc; #endif struct hsmmc *mmc_base; - unsigned int dsor = 0; - ulong start;
mmc_base = priv->base_addr; /* configue bus width */ @@ -801,28 +847,8 @@ static int omap_hsmmc_set_ios(struct udevice *dev) break; }
- /* configure clock with 96Mhz system clock. - */ - if (mmc->clock != 0) { - dsor = (MMC_CLOCK_REFERENCE * 1000000 / mmc->clock); - if ((MMC_CLOCK_REFERENCE * 1000000) / dsor > mmc->clock) - dsor++; - } - - mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK | CEN_MASK), - (ICE_STOP | DTO_15THDTO)); - - mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK, - (dsor << CLKD_OFFSET) | ICE_OSCILLATE); - - start = get_timer(0); - while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY) { - if (get_timer(0) - start > MAX_RETRY_MS) { - printf("%s: timedout waiting for ics!\n", __func__); - return -ETIMEDOUT; - } - } - writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl); + if (priv->clock != mmc->clock) + omap_hsmmc_set_clock(mmc);
return 0; }

On Thu, Sep 21, 2017 at 05:22:04PM +0200, Jean-Jacques Hiblot wrote:
From: Kishon Vijay Abraham I kishon@ti.com
Add a separate function for starting the clock, stopping the clock and setting the clock. Starting the clock and stopping the clock can be used irrespective of setting the clock (For example during iodelay recalibration). Also set the clock only if there is a change in frequency.
Signed-off-by: Kishon Vijay Abraham I kishon@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com
Can you please rebase and re-test this particular series? It no longer builds for am33xx for example, thanks!

From: Kishon Vijay Abraham I kishon@ti.com
No functional change. Move bus width configuration setting to a separate function and invoke it only if there is a change in the bus width.
Signed-off-by: Kishon Vijay Abraham I kishon@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- drivers/mmc/omap_hsmmc.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-)
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 6ef8295..bab0cef 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -62,6 +62,7 @@ struct omap_hsmmc_data { #if !CONFIG_IS_ENABLED(DM_MMC) struct mmc_config cfg; #endif + uint bus_width; uint clock; #ifdef OMAP_HSMMC_USE_GPIO #if CONFIG_IS_ENABLED(DM_MMC) @@ -810,17 +811,9 @@ static void omap_hsmmc_set_clock(struct mmc *mmc) omap_hsmmc_start_clock(mmc_base); }
-#if !CONFIG_IS_ENABLED(DM_MMC) -static int omap_hsmmc_set_ios(struct mmc *mmc) +static void omap_hsmmc_set_bus_width(struct mmc *mmc) { struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); -#else -static int omap_hsmmc_set_ios(struct udevice *dev) -{ - struct omap_hsmmc_data *priv = dev_get_priv(dev); - struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); - struct mmc *mmc = upriv->mmc; -#endif struct hsmmc *mmc_base;
mmc_base = priv->base_addr; @@ -847,6 +840,24 @@ static int omap_hsmmc_set_ios(struct udevice *dev) break; }
+ priv->bus_width = mmc->bus_width; +} + +#if !CONFIG_IS_ENABLED(DM_MMC) +static int omap_hsmmc_set_ios(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); +#else +static int omap_hsmmc_set_ios(struct udevice *dev) +{ + struct omap_hsmmc_data *priv = dev_get_priv(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct mmc *mmc = upriv->mmc; +#endif + + if (priv->bus_width != mmc->bus_width) + omap_hsmmc_set_bus_width(mmc); + if (priv->clock != mmc->clock) omap_hsmmc_set_clock(mmc);

From: Kishon Vijay Abraham I kishon@ti.com
"ti,dual-volt" is used in linux kernel to set the voltage capabilities. For host controller dt nodes that doesn't have "ti,dual-volt", it's assumed 1.8v is the io voltage. This is not always true (like in the case of beagle-x15 where the io lines are connected to 3.3v). Hence if "no-1-8-v" property is set, io voltage will be set to 3v.
Signed-off-by: Kishon Vijay Abraham I kishon@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- arch/arm/include/asm/omap_mmc.h | 12 ++++++-- drivers/mmc/omap_hsmmc.c | 67 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-)
diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index 102aec2..c4d326d 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -65,6 +65,7 @@ struct omap_hsmmc_plat { struct hsmmc *base_addr; struct mmc mmc; bool cd_inverted; + u32 controller_flags; };
/* @@ -124,8 +125,10 @@ struct omap_hsmmc_plat { #define DTW_8_BITMODE (0x1 << 5) /* CON[DW8]*/ #define SDBP_PWROFF (0x0 << 8) #define SDBP_PWRON (0x1 << 8) +#define SDVS_MASK (0x7 << 9) #define SDVS_1V8 (0x5 << 9) #define SDVS_3V0 (0x6 << 9) +#define SDVS_3V3 (0x7 << 9) #define DMA_SELECT (0x2 << 3) #define ICE_MASK (0x1 << 0) #define ICE_STOP (0x0 << 0) @@ -159,8 +162,13 @@ struct omap_hsmmc_plat { #define IE_CERR (0x01 << 28) #define IE_BADA (0x01 << 29)
-#define VS30_3V0SUP (1 << 25) -#define VS18_1V8SUP (1 << 26) +#define VS33_3V3SUP BIT(24) +#define VS30_3V0SUP BIT(25) +#define VS18_1V8SUP BIT(26) + +#define IOV_3V3 3300000 +#define IOV_3V0 3000000 +#define IOV_1V8 1800000
/* Driver definitions */ #define MMCSD_SECTOR_SIZE 512 diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index bab0cef..e0c7067 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -74,6 +74,9 @@ struct omap_hsmmc_data { int wp_gpio; #endif #endif +#if CONFIG_IS_ENABLED(DM_MMC) + uint iov; +#endif u8 controller_flags; #ifndef CONFIG_OMAP34XX struct omap_hsmmc_adma_desc *adma_desc_table; @@ -111,6 +114,8 @@ struct omap_hsmmc_adma_desc { * that the bandwidth is always above 3MB/s). */ #define DMA_TIMEOUT_PER_MB 333 +#define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0) +#define OMAP_HSMMC_NO_1_8_V BIT(1) #define OMAP_HSMMC_USE_ADMA BIT(2)
static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size); @@ -252,6 +257,58 @@ void mmc_init_stream(struct hsmmc *mmc_base) writel(readl(&mmc_base->con) & ~INIT_INITSTREAM, &mmc_base->con); }
+#if CONFIG_IS_ENABLED(DM_MMC) +static void omap_hsmmc_conf_bus_power(struct mmc *mmc) +{ + struct hsmmc *mmc_base; + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + u32 val; + + mmc_base = priv->base_addr; + + val = readl(&mmc_base->hctl) & ~SDVS_MASK; + + switch (priv->iov) { + case IOV_3V3: + val |= SDVS_3V3; + break; + case IOV_3V0: + val |= SDVS_3V0; + break; + case IOV_1V8: + val |= SDVS_1V8; + break; + } + + writel(val, &mmc_base->hctl); +} + +static void omap_hsmmc_set_capabilities(struct mmc *mmc) +{ + struct hsmmc *mmc_base; + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + u32 val; + + mmc_base = priv->base_addr; + val = readl(&mmc_base->capa); + + if (priv->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) { + val |= (VS30_3V0SUP | VS18_1V8SUP); + priv->iov = IOV_3V0; + } else if (priv->controller_flags & OMAP_HSMMC_NO_1_8_V) { + val |= VS30_3V0SUP; + val &= ~VS18_1V8SUP; + priv->iov = IOV_3V0; + } else { + val |= VS18_1V8SUP; + val &= ~VS30_3V0SUP; + priv->iov = IOV_1V8; + } + + writel(val, &mmc_base->capa); +} +#endif + static int omap_hsmmc_init_setup(struct mmc *mmc) { struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); @@ -286,9 +343,15 @@ static int omap_hsmmc_init_setup(struct mmc *mmc) if (reg_val & MADMA_EN) priv->controller_flags |= OMAP_HSMMC_USE_ADMA; #endif + +#if CONFIG_IS_ENABLED(DM_MMC) + omap_hsmmc_set_capabilities(mmc); + omap_hsmmc_conf_bus_power(mmc); +#else writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl); writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP, &mmc_base->capa); +#endif
reg_val = readl(&mmc_base->con) & RESERVED_MASK;
@@ -1067,6 +1130,10 @@ static int omap_hsmmc_ofdata_to_platdata(struct udevice *dev) cfg->f_max = fdtdec_get_int(fdt, node, "max-frequency", 52000000); cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; + if (fdtdec_get_bool(fdt, node, "ti,dual-volt")) + plat->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT; + if (fdtdec_get_bool(fdt, node, "no-1-8-v")) + plat->controller_flags |= OMAP_HSMMC_NO_1_8_V;
#ifdef OMAP_HSMMC_USE_GPIO plat->cd_inverted = fdtdec_get_bool(fdt, node, "cd-inverted");

From: Kishon Vijay Abraham I kishon@ti.com
Use the timing parameter set in the MMC core to set the mode in UHSMS bit field. This is in preparation for adding HS200 support in omap hsmmc driver.
Signed-off-by: Kishon Vijay Abraham I kishon@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- arch/arm/include/asm/omap_mmc.h | 12 ++++++++++- drivers/mmc/omap_hsmmc.c | 47 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-)
diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index c4d326d..3f94f2e 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -53,7 +53,8 @@ struct hsmmc { unsigned int sysctl; /* 0x12C */ unsigned int stat; /* 0x130 */ unsigned int ie; /* 0x134 */ - unsigned char res4[0x8]; + unsigned char res4[0x4]; + unsigned int ac12; /* 0x13C */ unsigned int capa; /* 0x140 */ unsigned char res5[0x10]; unsigned int admaes; /* 0x154 */ @@ -170,6 +171,15 @@ struct omap_hsmmc_plat { #define IOV_3V0 3000000 #define IOV_1V8 1800000
+#define AC12_ET (1 << 22) +#define AC12_UHSMC_MASK (7 << 16) +#define AC12_UHSMC_DDR50 (4 << 16) +#define AC12_UHSMC_SDR104 (3 << 16) +#define AC12_UHSMC_SDR50 (2 << 16) +#define AC12_UHSMC_SDR25 (1 << 16) +#define AC12_UHSMC_SDR12 (0 << 16) +#define AC12_UHSMC_RES (0x7 << 16) + /* Driver definitions */ #define MMCSD_SECTOR_SIZE 512 #define MMC_CARD 0 diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index e0c7067..4a65a46 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -76,6 +76,7 @@ struct omap_hsmmc_data { #endif #if CONFIG_IS_ENABLED(DM_MMC) uint iov; + enum bus_mode mode; #endif u8 controller_flags; #ifndef CONFIG_OMAP34XX @@ -258,6 +259,48 @@ void mmc_init_stream(struct hsmmc *mmc_base) }
#if CONFIG_IS_ENABLED(DM_MMC) +static void omap_hsmmc_set_timing(struct mmc *mmc) +{ + u32 val; + struct hsmmc *mmc_base; + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + + mmc_base = priv->base_addr; + + val = readl(&mmc_base->ac12); + val &= ~AC12_UHSMC_MASK; + priv->mode = mmc->selected_mode; + + switch (priv->mode) { + case MMC_HS_200: + case UHS_SDR104: + val |= AC12_UHSMC_SDR104; + break; + case UHS_SDR50: + val |= AC12_UHSMC_SDR50; + break; + case MMC_DDR_52: + case UHS_DDR50: + val |= AC12_UHSMC_DDR50; + break; + case SD_HS: + case MMC_HS_52: + case UHS_SDR25: + val |= AC12_UHSMC_SDR25; + break; + case MMC_LEGACY: + case MMC_HS: + case SD_LEGACY: + case UHS_SDR12: + val |= AC12_UHSMC_SDR12; + break; + default: + val |= AC12_UHSMC_RES; + break; + } + writel(val, &mmc_base->ac12); +} + static void omap_hsmmc_conf_bus_power(struct mmc *mmc) { struct hsmmc *mmc_base; @@ -924,6 +967,10 @@ static int omap_hsmmc_set_ios(struct udevice *dev) if (priv->clock != mmc->clock) omap_hsmmc_set_clock(mmc);
+#if CONFIG_IS_ENABLED(DM_MMC) + if (priv->mode != mmc->selected_mode) + omap_hsmmc_set_timing(mmc); +#endif return 0; }

From: Kishon Vijay Abraham I kishon@ti.com
In order to enable DDR mode, Dual Data Rate mode bit has to be set in MMCHS_CON register. Set it here.
Signed-off-by: Kishon Vijay Abraham I kishon@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- arch/arm/include/asm/omap_mmc.h | 1 + drivers/mmc/omap_hsmmc.c | 5 +++++ 2 files changed, 6 insertions(+)
diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index 3f94f2e..341a2e2 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -89,6 +89,7 @@ struct omap_hsmmc_plat { #define WPP_ACTIVEHIGH (0x0 << 8) #define RESERVED_MASK (0x3 << 9) #define CTPL_MMC_SD (0x0 << 11) +#define DDR (0x1 << 19) #define DMA_MASTER (0x1 << 20) #define BLEN_512BYTESLEN (0x200 << 0) #define NBLK_STPCNT (0x0 << 16) diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 4a65a46..d5cd826 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -271,6 +271,11 @@ static void omap_hsmmc_set_timing(struct mmc *mmc) val &= ~AC12_UHSMC_MASK; priv->mode = mmc->selected_mode;
+ if (mmc_is_mode_ddr(priv->mode)) + writel(readl(&mmc_base->con) | DDR, &mmc_base->con); + else + writel(readl(&mmc_base->con) & ~DDR, &mmc_base->con); + switch (priv->mode) { case MMC_HS_200: case UHS_SDR104:

From: Kishon Vijay Abraham I kishon@ti.com
HS200/SDR104 requires tuning command to be sent to the card. Use the mmc_send_tuning library function to send the tuning command and configure the internal DLL.
Signed-off-by: Kishon Vijay Abraham I kishon@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- arch/arm/include/asm/omap_mmc.h | 21 ++++++- drivers/mmc/omap_hsmmc.c | 118 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 2 deletions(-)
diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index 341a2e2..0293281 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -39,7 +39,9 @@ struct hsmmc { unsigned int sysstatus; /* 0x14 */ unsigned char res2[0x14]; unsigned int con; /* 0x2C */ - unsigned char res3[0xD4]; + unsigned int pwcnt; /* 0x30 */ + unsigned int dll; /* 0x34 */ + unsigned char res3[0xcc]; unsigned int blk; /* 0x104 */ unsigned int arg; /* 0x108 */ unsigned int cmd; /* 0x10C */ @@ -56,7 +58,8 @@ struct hsmmc { unsigned char res4[0x4]; unsigned int ac12; /* 0x13C */ unsigned int capa; /* 0x140 */ - unsigned char res5[0x10]; + unsigned int capa2; /* 0x144 */ + unsigned char res5[0xc]; unsigned int admaes; /* 0x154 */ unsigned int admasal; /* 0x158 */ }; @@ -173,6 +176,8 @@ struct omap_hsmmc_plat { #define IOV_1V8 1800000
#define AC12_ET (1 << 22) +#define AC12_V1V8_SIGEN (1 << 19) +#define AC12_SCLK_SEL (1 << 23) #define AC12_UHSMC_MASK (7 << 16) #define AC12_UHSMC_DDR50 (4 << 16) #define AC12_UHSMC_SDR104 (3 << 16) @@ -199,6 +204,18 @@ struct omap_hsmmc_plat { /* Clock Configurations and Macros */ #define MMC_CLOCK_REFERENCE 96 /* MHz */
+/* DLL */ +#define DLL_SWT (1 << 20) +#define DLL_FORCE_SR_C_SHIFT 13 +#define DLL_FORCE_SR_C_MASK 0x7f +#define DLL_FORCE_VALUE (1 << 12) +#define DLL_CALIB (1 << 1) + +#define MAX_PHASE_DELAY 0x7c + +/* CAPA2 */ +#define CAPA2_TSDR50 (1 << 13) + #define mmc_reg_out(addr, mask, val)\ writel((readl(addr) & (~(mask))) | ((val) & (mask)), (addr))
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index d5cd826..321a091 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -124,6 +124,7 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, unsigned int siz); static void omap_hsmmc_start_clock(struct hsmmc *mmc_base); static void omap_hsmmc_stop_clock(struct hsmmc *mmc_base); +static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit);
static inline struct omap_hsmmc_data *omap_hsmmc_get_data(struct mmc *mmc) { @@ -355,6 +356,122 @@ static void omap_hsmmc_set_capabilities(struct mmc *mmc)
writel(val, &mmc_base->capa); } + +static void omap_hsmmc_disable_tuning(struct mmc *mmc) +{ + struct hsmmc *mmc_base; + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + u32 val; + + mmc_base = priv->base_addr; + val = readl(&mmc_base->ac12); + val &= ~(AC12_SCLK_SEL); + writel(val, &mmc_base->ac12); + + val = readl(&mmc_base->dll); + val &= ~(DLL_FORCE_VALUE | DLL_SWT); + writel(val, &mmc_base->dll); +} + +static void omap_hsmmc_set_dll(struct mmc *mmc, int count) +{ + int i; + struct hsmmc *mmc_base; + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + u32 val; + + mmc_base = priv->base_addr; + val = readl(&mmc_base->dll); + val |= DLL_FORCE_VALUE; + val &= ~(DLL_FORCE_SR_C_MASK << DLL_FORCE_SR_C_SHIFT); + val |= (count << DLL_FORCE_SR_C_SHIFT); + writel(val, &mmc_base->dll); + + val |= DLL_CALIB; + writel(val, &mmc_base->dll); + for (i = 0; i < 1000; i++) { + if (readl(&mmc_base->dll) & DLL_CALIB) + break; + } + val &= ~DLL_CALIB; + writel(val, &mmc_base->dll); +} + +static int omap_hsmmc_execute_tuning(struct udevice *dev, uint opcode) +{ + struct omap_hsmmc_data *priv = dev_get_priv(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct mmc *mmc = upriv->mmc; + struct hsmmc *mmc_base; + u32 val; + u8 cur_match, prev_match = 0; + int ret; + u32 phase_delay = 0; + u32 start_window = 0, max_window = 0; + u32 length = 0, max_len = 0; + + mmc_base = priv->base_addr; + val = readl(&mmc_base->capa2); + + /* clock tuning is not needed for upto 52MHz */ + if (!((mmc->selected_mode == MMC_HS_200) || + (mmc->selected_mode == UHS_SDR104) || + ((mmc->selected_mode == UHS_SDR50) && (val & CAPA2_TSDR50)))) + return 0; + + val = readl(&mmc_base->dll); + val |= DLL_SWT; + writel(val, &mmc_base->dll); + while (phase_delay <= MAX_PHASE_DELAY) { + omap_hsmmc_set_dll(mmc, phase_delay); + + cur_match = !mmc_send_tuning(mmc, opcode, NULL); + + if (cur_match) { + if (prev_match) { + length++; + } else { + start_window = phase_delay; + length = 1; + } + } + + if (length > max_len) { + max_window = start_window; + max_len = length; + } + + prev_match = cur_match; + phase_delay += 4; + } + + if (!max_len) { + ret = -EIO; + goto tuning_error; + } + + val = readl(&mmc_base->ac12); + if (!(val & AC12_SCLK_SEL)) { + ret = -EIO; + goto tuning_error; + } + + phase_delay = max_window + 4 * ((3 * max_len) >> 2); + omap_hsmmc_set_dll(mmc, phase_delay); + + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD); + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC); + + return 0; + +tuning_error: + + omap_hsmmc_disable_tuning(mmc); + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD); + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC); + + return ret; +} #endif
static int omap_hsmmc_init_setup(struct mmc *mmc) @@ -1046,6 +1163,7 @@ static const struct dm_mmc_ops omap_hsmmc_ops = { .get_cd = omap_hsmmc_getcd, .get_wp = omap_hsmmc_getwp, #endif + .execute_tuning = omap_hsmmc_execute_tuning, }; #else static const struct mmc_ops omap_hsmmc_ops = {

From: Kishon Vijay Abraham I kishon@ti.com
According to errata i802, DCRC error interrupts (MMCHS_STAT[21] DCRC=0x1) can occur during the tuning procedure.
The DCRC interrupt, occurs when the last tuning block fails (the last ratio tested). The delay from CRC check until the interrupt is asserted is bigger than the delay until assertion of the tuning end flag. Assertion of tuning end flag is what masks the interrupts. Because of this race, an erroneous DCRC interrupt occurs.
The suggested workaround is to disable DCRC interrupts during the tuning procedure which is implemented here.
Signed-off-by: Kishon Vijay Abraham I kishon@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- arch/arm/include/asm/omap_mmc.h | 4 ++++ drivers/mmc/omap_hsmmc.c | 26 ++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index 0293281..0893844 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -219,6 +219,10 @@ struct omap_hsmmc_plat { #define mmc_reg_out(addr, mask, val)\ writel((readl(addr) & (~(mask))) | ((val) & (mask)), (addr))
+#define INT_EN_MASK (IE_BADA | IE_CERR | IE_DEB | IE_DCRC |\ + IE_DTO | IE_CIE | IE_CEB | IE_CCRC | IE_ADMAE | IE_CTO |\ + IE_BRR | IE_BWR | IE_TC | IE_CC) + int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, int wp_gpio);
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 321a091..8e42410 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -474,6 +474,25 @@ tuning_error: } #endif
+static void mmc_enable_irq(struct mmc *mmc, struct mmc_cmd *cmd) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + struct hsmmc *mmc_base = priv->base_addr; + u32 irq_mask = INT_EN_MASK; + + /* + * TODO: Errata i802 indicates only DCRC interrupts can occur during + * tuning procedure and DCRC should be disabled. But see occurences + * of DEB, CIE, CEB, CCRC interupts during tuning procedure. These + * interrupts occur along with BRR, so the data is actually in the + * buffer. It has to be debugged why these interrutps occur + */ + if (cmd && mmc_is_tuning_cmd(cmd->cmdidx)) + irq_mask &= ~(IE_DEB | IE_DCRC | IE_CIE | IE_CEB | IE_CCRC); + + writel(irq_mask, &mmc_base->ie); +} + static int omap_hsmmc_init_setup(struct mmc *mmc) { struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); @@ -540,10 +559,7 @@ static int omap_hsmmc_init_setup(struct mmc *mmc)
writel(readl(&mmc_base->hctl) | SDBP_PWRON, &mmc_base->hctl);
- writel(IE_BADA | IE_CERR | IE_DEB | IE_DCRC | IE_DTO | IE_CIE | - IE_CEB | IE_CCRC | IE_ADMAE | IE_CTO | IE_BRR | IE_BWR | IE_TC | - IE_CC, &mmc_base->ie); - + mmc_enable_irq(mmc, NULL); mmc_init_stream(mmc_base);
return 0; @@ -806,6 +822,8 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, #endif }
+ mmc_enable_irq(mmc, cmd); + writel(cmd->cmdarg, &mmc_base->arg); udelay(20); /* To fix "No status update" error on eMMC */ writel((cmd->cmdidx << 24) | flags, &mmc_base->cmd);

From OMAP3 SoCs (OMAP3, OMAP4, OMAP5, AM572x, AM571x), the DAT/CMD lines
reset procedure section in TRM suggests to first poll the SRD/SRC bit until it is set to 0x1. But looks like that bit is never set to 1 and there is an observable delay of 1sec everytime the driver tries to reset DAT/CMD. (The same is observed in linux kernel).
Reduce the time the driver waits for the controller to set the SRC/SRD bits to 1 so that there is no observable delay.
Signed-off-by: Kishon Vijay Abraham I kishon@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- drivers/mmc/omap_hsmmc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 8e42410..116aa31 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -108,6 +108,7 @@ struct omap_hsmmc_adma_desc {
/* If we fail after 1 second wait, something is really bad */ #define MAX_RETRY_MS 1000 +#define MMC_TIMEOUT_MS 20
/* DMA transfers can take a long time if a lot a data is transfered. * The timeout must take in account the amount of data. Let's assume @@ -596,7 +597,7 @@ static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit) if (!(readl(&mmc_base->sysctl) & bit)) { start = get_timer(0); while (!(readl(&mmc_base->sysctl) & bit)) { - if (get_timer(0) - start > MAX_RETRY_MS) + if (get_timer(0) - start > MMC_TIMEOUT_MS) return; } }

From: Kishon Vijay Abraham I kishon@ti.com
Use the mmc_of_parse library function to populate mmc_config instead of repeating the same code in host controller driver.
Signed-off-by: Kishon Vijay Abraham I kishon@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- drivers/mmc/omap_hsmmc.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-)
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 116aa31..734854b 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -1291,32 +1291,18 @@ static int omap_hsmmc_ofdata_to_platdata(struct udevice *dev) struct mmc_config *cfg = &plat->cfg; const void *fdt = gd->fdt_blob; int node = dev_of_offset(dev); - int val; + int ret;
plat->base_addr = map_physmem(devfdt_get_addr(dev), sizeof(struct hsmmc *), MAP_NOCACHE);
- cfg->host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS; - val = fdtdec_get_int(fdt, node, "bus-width", -1); - if (val < 0) { - printf("error: bus-width property missing\n"); - return -ENOENT; - } - - switch (val) { - case 0x8: - cfg->host_caps |= MMC_MODE_8BIT; - case 0x4: - cfg->host_caps |= MMC_MODE_4BIT; - break; - default: - printf("error: invalid bus-width property\n"); - return -ENOENT; - } + ret = mmc_of_parse(fdt, node, cfg); + if (ret < 0) + return ret;
+ cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; cfg->f_min = 400000; - cfg->f_max = fdtdec_get_int(fdt, node, "max-frequency", 52000000); cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; if (fdtdec_get_bool(fdt, node, "ti,dual-volt"))

From: Kishon Vijay Abraham I kishon@ti.com
Add a new API to perform iodelay recalibration without isolate io to be used in uboot.
The data manual of J6/J6 Eco recommends to set different IODELAY values depending on the mode in which the MMC/SD is enumerated in order to ensure IO timings are met. The MMC driver can use the new API to set the IO delay values depending on the MMC mode.
Signed-off-by: Kishon Vijay Abraham I kishon@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h | 3 +++ arch/arm/mach-omap2/omap5/dra7xx_iodelay.c | 30 ++++++++++++++++++++++++ include/configs/am57xx_evm.h | 2 -- include/configs/dra7xx_evm.h | 2 -- 4 files changed, 33 insertions(+), 4 deletions(-)
diff --git a/arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h b/arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h index c997004..a8780ee 100644 --- a/arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h +++ b/arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h @@ -83,6 +83,9 @@ void __recalibrate_iodelay(struct pad_conf_entry const *pad, int npads, struct iodelay_cfg_entry const *iodelay, int niodelays); +void late_recalibrate_iodelay(struct pad_conf_entry const *pad, int npads, + struct iodelay_cfg_entry const *iodelay, + int niodelays); int __recalibrate_iodelay_start(void); void __recalibrate_iodelay_end(int ret);
diff --git a/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c b/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c index 8798730..a9a9f75 100644 --- a/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c +++ b/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c @@ -272,3 +272,33 @@ err: __recalibrate_iodelay_end(ret);
} + +void late_recalibrate_iodelay(struct pad_conf_entry const *pad, int npads, + struct iodelay_cfg_entry const *iodelay, + int niodelays) +{ + int ret = 0; + + /* unlock IODELAY CONFIG registers */ + writel(CFG_IODELAY_UNLOCK_KEY, (*ctrl)->iodelay_config_base + + CFG_REG_8_OFFSET); + + ret = calibrate_iodelay((*ctrl)->iodelay_config_base); + if (ret) + goto err; + + ret = update_delay_mechanism((*ctrl)->iodelay_config_base); + + /* Configure Mux settings */ + do_set_mux32((*ctrl)->control_padconf_core_base, pad, npads); + + /* Configure Manual IO timing modes */ + ret = do_set_iodelay((*ctrl)->iodelay_config_base, iodelay, niodelays); + if (ret) + goto err; + +err: + /* lock IODELAY CONFIG registers */ + writel(CFG_IODELAY_LOCK_KEY, (*ctrl)->iodelay_config_base + + CFG_REG_8_OFFSET); +} diff --git a/include/configs/am57xx_evm.h b/include/configs/am57xx_evm.h index 0c70c53..4125391 100644 --- a/include/configs/am57xx_evm.h +++ b/include/configs/am57xx_evm.h @@ -15,9 +15,7 @@ #include <environment/ti/dfu.h> #include <linux/sizes.h>
-#ifdef CONFIG_SPL_BUILD #define CONFIG_IODELAY_RECALIBRATION -#endif
#define CONFIG_NR_DRAM_BANKS 2
diff --git a/include/configs/dra7xx_evm.h b/include/configs/dra7xx_evm.h index 6c0fc35..435284a 100644 --- a/include/configs/dra7xx_evm.h +++ b/include/configs/dra7xx_evm.h @@ -14,9 +14,7 @@
#include <environment/ti/dfu.h>
-#ifdef CONFIG_SPL_BUILD #define CONFIG_IODELAY_RECALIBRATION -#endif
#define CONFIG_VERY_BIG_RAM #define CONFIG_NR_DRAM_BANKS 2

From: Kishon Vijay Abraham I kishon@ti.com
The data manual of J6/J6 Eco recommends to set different IODELAY values depending on the mode in which the MMC/SD is enumerated in order to ensure IO timings are met.
Add support to parse mux values and iodelay values from device tree and set these depending on the enumerated MMC mode.
Signed-off-by: Kishon Vijay Abraham I kishon@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- drivers/mmc/omap_hsmmc.c | 372 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 372 insertions(+)
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 734854b..67ba31e 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -34,6 +34,10 @@ #include <palmas.h> #include <asm/io.h> #include <asm/arch/mmc_host_def.h> +#ifdef CONFIG_OMAP54XX +#include <asm/arch/mux_dra7xx.h> +#include <asm/arch/dra7xx_iodelay.h> +#endif #if !defined(CONFIG_SOC_KEYSTONE) #include <asm/gpio.h> #include <asm/arch/sys_proto.h> @@ -57,6 +61,15 @@ DECLARE_GLOBAL_DATA_PTR; #define SYSCTL_SRC (1 << 25) #define SYSCTL_SRD (1 << 26)
+#ifdef CONFIG_IODELAY_RECALIBRATION +struct omap_hsmmc_pinctrl_state { + struct pad_conf_entry *padconf; + int npads; + struct iodelay_cfg_entry *iodelay; + int niodelays; +}; +#endif + struct omap_hsmmc_data { struct hsmmc *base_addr; #if !CONFIG_IS_ENABLED(DM_MMC) @@ -83,6 +96,17 @@ struct omap_hsmmc_data { struct omap_hsmmc_adma_desc *adma_desc_table; uint desc_slot; #endif +#ifdef CONFIG_IODELAY_RECALIBRATION + struct omap_hsmmc_pinctrl_state *default_pinctrl_state; + struct omap_hsmmc_pinctrl_state *hs_pinctrl_state; + struct omap_hsmmc_pinctrl_state *hs200_1_8v_pinctrl_state; + struct omap_hsmmc_pinctrl_state *ddr_1_8v_pinctrl_state; + struct omap_hsmmc_pinctrl_state *sdr12_pinctrl_state; + struct omap_hsmmc_pinctrl_state *sdr25_pinctrl_state; + struct omap_hsmmc_pinctrl_state *ddr50_pinctrl_state; + struct omap_hsmmc_pinctrl_state *sdr50_pinctrl_state; + struct omap_hsmmc_pinctrl_state *sdr104_pinctrl_state; +#endif };
#ifndef CONFIG_OMAP34XX @@ -93,6 +117,10 @@ struct omap_hsmmc_adma_desc { u32 addr; };
+struct omap_mmc_of_data { + u8 controller_flags; +}; + #define ADMA_MAX_LEN 63488
/* Decriptor table defines */ @@ -119,6 +147,7 @@ struct omap_hsmmc_adma_desc { #define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0) #define OMAP_HSMMC_NO_1_8_V BIT(1) #define OMAP_HSMMC_USE_ADMA BIT(2) +#define OMAP_HSMMC_REQUIRE_IODELAY BIT(3)
static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size); static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, @@ -261,6 +290,56 @@ void mmc_init_stream(struct hsmmc *mmc_base) }
#if CONFIG_IS_ENABLED(DM_MMC) +#ifdef CONFIG_IODELAY_RECALIBRATION +static void omap_hsmmc_io_recalibrate(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + struct omap_hsmmc_pinctrl_state *pinctrl_state; + + switch (priv->mode) { + case MMC_HS_200: + pinctrl_state = priv->hs200_1_8v_pinctrl_state; + break; + case UHS_SDR104: + pinctrl_state = priv->sdr104_pinctrl_state; + break; + case UHS_SDR50: + pinctrl_state = priv->sdr50_pinctrl_state; + break; + case UHS_DDR50: + pinctrl_state = priv->ddr50_pinctrl_state; + break; + case UHS_SDR25: + pinctrl_state = priv->sdr25_pinctrl_state; + break; + case UHS_SDR12: + pinctrl_state = priv->sdr12_pinctrl_state; + break; + case SD_HS: + case MMC_HS: + case MMC_HS_52: + pinctrl_state = priv->hs_pinctrl_state; + break; + case MMC_DDR_52: + pinctrl_state = priv->ddr_1_8v_pinctrl_state; + default: + pinctrl_state = priv->default_pinctrl_state; + break; + } + + if (priv->controller_flags & OMAP_HSMMC_REQUIRE_IODELAY) { + if (pinctrl_state->iodelay) + late_recalibrate_iodelay(pinctrl_state->padconf, + pinctrl_state->npads, + pinctrl_state->iodelay, + pinctrl_state->niodelays); + else + do_set_mux32((*ctrl)->control_padconf_core_base, + pinctrl_state->padconf, + pinctrl_state->npads); + } +} +#endif static void omap_hsmmc_set_timing(struct mmc *mmc) { u32 val; @@ -269,6 +348,7 @@ static void omap_hsmmc_set_timing(struct mmc *mmc)
mmc_base = priv->base_addr;
+ omap_hsmmc_stop_clock(mmc_base); val = readl(&mmc_base->ac12); val &= ~AC12_UHSMC_MASK; priv->mode = mmc->selected_mode; @@ -306,6 +386,11 @@ static void omap_hsmmc_set_timing(struct mmc *mmc) break; } writel(val, &mmc_base->ac12); + +#ifdef CONFIG_IODELAY_RECALIBRATION + omap_hsmmc_io_recalibrate(mmc); +#endif + omap_hsmmc_start_clock(mmc_base); }
static void omap_hsmmc_conf_bus_power(struct mmc *mmc) @@ -1284,10 +1369,271 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, return 0; } #else + +#ifdef CONFIG_IODELAY_RECALIBRATION +static struct pad_conf_entry * +omap_hsmmc_get_pad_conf_entry(const fdt32_t *pinctrl, int count) +{ + int index = 0; + struct pad_conf_entry *padconf; + + padconf = (struct pad_conf_entry *)malloc(sizeof(*padconf) * count); + if (!padconf) { + debug("failed to allocate memory\n"); + return 0; + } + + while (index < count) { + padconf[index].offset = fdt32_to_cpu(pinctrl[2 * index]); + padconf[index].val = fdt32_to_cpu(pinctrl[2 * index + 1]); + index++; + } + + return padconf; +} + +static struct iodelay_cfg_entry * +omap_hsmmc_get_iodelay_cfg_entry(const fdt32_t *pinctrl, int count) +{ + int index = 0; + struct iodelay_cfg_entry *iodelay; + + iodelay = (struct iodelay_cfg_entry *)malloc(sizeof(*iodelay) * count); + if (!iodelay) { + debug("failed to allocate memory\n"); + return 0; + } + + while (index < count) { + iodelay[index].offset = fdt32_to_cpu(pinctrl[3 * index]); + iodelay[index].a_delay = fdt32_to_cpu(pinctrl[3 * index + 1]); + iodelay[index].g_delay = fdt32_to_cpu(pinctrl[3 * index + 2]); + index++; + } + + return iodelay; +} + +static const fdt32_t *omap_hsmmc_get_pinctrl_entry(u32 phandle, + const char *name, int *len) +{ + const void *fdt = gd->fdt_blob; + int offset; + const fdt32_t *pinctrl; + + offset = fdt_node_offset_by_phandle(fdt, phandle); + if (offset < 0) { + debug("failed to get pinctrl node %s.\n", + fdt_strerror(offset)); + return 0; + } + + pinctrl = fdt_getprop(fdt, offset, name, len); + if (!pinctrl) { + debug("failed to get property %s\n", name); + return 0; + } + + return pinctrl; +} + +static uint32_t omap_hsmmc_get_pad_conf_phandle(struct mmc *mmc, + char *prop_name) +{ + const void *fdt = gd->fdt_blob; + const __be32 *phandle; + int node = dev_of_offset(mmc->dev); + + phandle = fdt_getprop(fdt, node, prop_name, NULL); + if (!phandle) { + debug("failed to get property %s\n", prop_name); + return 0; + } + + return fdt32_to_cpu(*phandle); +} + +static uint32_t omap_hsmmc_get_iodelay_phandle(struct mmc *mmc, + char *prop_name) +{ + const void *fdt = gd->fdt_blob; + const __be32 *phandle; + int len; + int count; + int node = dev_of_offset(mmc->dev); + + phandle = fdt_getprop(fdt, node, prop_name, &len); + if (!phandle) { + debug("failed to get property %s\n", prop_name); + return 0; + } + + /* No manual mode iodelay values if count < 2 */ + count = len / sizeof(*phandle); + if (count < 2) + return 0; + + return fdt32_to_cpu(*(phandle + 1)); +} + +static struct pad_conf_entry * +omap_hsmmc_get_pad_conf(struct mmc *mmc, char *prop_name, int *npads) +{ + int len; + int count; + struct pad_conf_entry *padconf; + u32 phandle; + const fdt32_t *pinctrl; + + phandle = omap_hsmmc_get_pad_conf_phandle(mmc, prop_name); + if (!phandle) + return ERR_PTR(-EINVAL); + + pinctrl = omap_hsmmc_get_pinctrl_entry(phandle, "pinctrl-single,pins", + &len); + if (!pinctrl) + return ERR_PTR(-EINVAL); + + count = (len / sizeof(*pinctrl)) / 2; + padconf = omap_hsmmc_get_pad_conf_entry(pinctrl, count); + if (!padconf) + return ERR_PTR(-EINVAL); + + *npads = count; + + return padconf; +} + +static struct iodelay_cfg_entry * +omap_hsmmc_get_iodelay(struct mmc *mmc, char *prop_name, int *niodelay) +{ + int len; + int count; + struct iodelay_cfg_entry *iodelay; + u32 phandle; + const fdt32_t *pinctrl; + + phandle = omap_hsmmc_get_iodelay_phandle(mmc, prop_name); + /* Not all modes have manual mode iodelay values. So its not fatal */ + if (!phandle) + return 0; + + pinctrl = omap_hsmmc_get_pinctrl_entry(phandle, "pinctrl-pin-array", + &len); + if (!pinctrl) + return ERR_PTR(-EINVAL); + + count = (len / sizeof(*pinctrl)) / 3; + iodelay = omap_hsmmc_get_iodelay_cfg_entry(pinctrl, count); + if (!iodelay) + return ERR_PTR(-EINVAL); + + *niodelay = count; + + return iodelay; +} + +static struct omap_hsmmc_pinctrl_state * +omap_hsmmc_get_pinctrl_by_mode(struct mmc *mmc, char *mode) +{ + int index; + int npads = 0; + int niodelays = 0; + const void *fdt = gd->fdt_blob; + int node = dev_of_offset(mmc->dev); + char prop_name[11]; + struct omap_hsmmc_pinctrl_state *pinctrl_state; + + pinctrl_state = (struct omap_hsmmc_pinctrl_state *) + malloc(sizeof(*pinctrl_state)); + if (!pinctrl_state) { + debug("failed to allocate memory\n"); + return 0; + } + + index = fdt_stringlist_search(fdt, node, "pinctrl-names", mode); + if (index < 0) { + debug("fail to find %s mode %s\n", mode, fdt_strerror(index)); + goto err_pinctrl_state; + } + + sprintf(prop_name, "pinctrl-%d", index); + + pinctrl_state->padconf = omap_hsmmc_get_pad_conf(mmc, prop_name, + &npads); + if (IS_ERR(pinctrl_state->padconf)) + goto err_pinctrl_state; + pinctrl_state->npads = npads; + + pinctrl_state->iodelay = omap_hsmmc_get_iodelay(mmc, prop_name, + &niodelays); + if (IS_ERR(pinctrl_state->iodelay)) + goto err_padconf; + pinctrl_state->niodelays = niodelays; + + return pinctrl_state; + +err_padconf: + kfree(pinctrl_state->padconf); + +err_pinctrl_state: + kfree(pinctrl_state); + return 0; +} + +#define OMAP_HSMMC_SETUP_PINCTRL(capmask, mode) \ + do { \ + struct omap_hsmmc_pinctrl_state *s; \ + if (!(cfg->host_caps & capmask)) \ + break; \ + \ + s = omap_hsmmc_get_pinctrl_by_mode(mmc, #mode); \ + if (!s) { \ + debug("%s: no pinctrl for %s\n", \ + mmc->dev->name, #mode); \ + cfg->host_caps &= ~(capmask); \ + } else { \ + priv->mode##_pinctrl_state = s; \ + } \ + } while (0) + +static int omap_hsmmc_get_pinctrl_state(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + struct mmc_config *cfg = omap_hsmmc_get_cfg(mmc); + struct omap_hsmmc_pinctrl_state *default_pinctrl; + + if (!(priv->controller_flags & OMAP_HSMMC_REQUIRE_IODELAY)) + return 0; + + default_pinctrl = omap_hsmmc_get_pinctrl_by_mode(mmc, "default"); + if (!default_pinctrl) { + printf("no pinctrl state for default mode\n"); + return -EINVAL; + } + + priv->default_pinctrl_state = default_pinctrl; + + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR104), sdr104); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR50), sdr50); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_DDR50), ddr50); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR25), sdr25); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR12), sdr12); + + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(MMC_HS_200), hs200_1_8v); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(MMC_DDR_52), ddr_1_8v); + OMAP_HSMMC_SETUP_PINCTRL(MMC_MODE_HS, hs); + + return 0; +} +#endif + #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) static int omap_hsmmc_ofdata_to_platdata(struct udevice *dev) { struct omap_hsmmc_plat *plat = dev_get_platdata(dev); + struct omap_mmc_of_data *of_data = (void *)dev_get_driver_data(dev); + struct mmc_config *cfg = &plat->cfg; const void *fdt = gd->fdt_blob; int node = dev_of_offset(dev); @@ -1309,6 +1655,8 @@ static int omap_hsmmc_ofdata_to_platdata(struct udevice *dev) plat->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT; if (fdtdec_get_bool(fdt, node, "no-1-8-v")) plat->controller_flags |= OMAP_HSMMC_NO_1_8_V; + if (of_data) + plat->controller_flags |= of_data->controller_flags;
#ifdef OMAP_HSMMC_USE_GPIO plat->cd_inverted = fdtdec_get_bool(fdt, node, "cd-inverted"); @@ -1334,9 +1682,13 @@ static int omap_hsmmc_probe(struct udevice *dev) struct omap_hsmmc_data *priv = dev_get_priv(dev); struct mmc_config *cfg = &plat->cfg; struct mmc *mmc; +#ifdef CONFIG_IODELAY_RECALIBRATION + int ret; +#endif
cfg->name = "OMAP SD/MMC"; priv->base_addr = plat->base_addr; + priv->controller_flags = plat->controller_flags; #ifdef OMAP_HSMMC_USE_GPIO priv->cd_inverted = plat->cd_inverted; #endif @@ -1357,14 +1709,34 @@ static int omap_hsmmc_probe(struct udevice *dev) mmc->dev = dev; upriv->mmc = mmc;
+#ifdef CONFIG_IODELAY_RECALIBRATION + ret = omap_hsmmc_get_pinctrl_state(mmc); + /* + * disable high speed modes for the platforms that require IO delay + * and for which we don't have this information + */ + if ((ret < 0) && + (priv->controller_flags & OMAP_HSMMC_REQUIRE_IODELAY)) { + priv->controller_flags &= ~OMAP_HSMMC_REQUIRE_IODELAY; + cfg->host_caps &= ~(MMC_CAP(MMC_HS_200) | MMC_CAP(MMC_DDR_52) | + UHS_CAPS); + } +#endif + return omap_hsmmc_init_setup(mmc); }
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) + +static const struct omap_mmc_of_data dra7_mmc_of_data = { + .controller_flags = OMAP_HSMMC_REQUIRE_IODELAY, +}; + static const struct udevice_id omap_hsmmc_ids[] = { { .compatible = "ti,omap3-hsmmc" }, { .compatible = "ti,omap4-hsmmc" }, { .compatible = "ti,am33xx-hsmmc" }, + { .compatible = "ti,dra7-hsmmc", .data = (ulong)&dra7_mmc_of_data }, { } }; #endif

From: Kishon Vijay Abraham I kishon@ti.com
AM572x SR1.1 requires different IODelay values to be used than that used in AM572x SR2.0. These values are populated in device tree. Add capability in omap_hsmmc driver to extract IOdelay values for different silicon revision. The maximum frequency is also reduced when using a ES1.1. To keep the ability to boot both revsions with the same dtb, those values can be provided by the platform code.
Signed-off-by: Kishon Vijay Abraham I kishon@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- arch/arm/include/asm/arch-omap5/sys_proto.h | 7 ++++ arch/arm/include/asm/omap_mmc.h | 1 + drivers/mmc/omap_hsmmc.c | 58 ++++++++++++++++++++++------- 3 files changed, 52 insertions(+), 14 deletions(-)
diff --git a/arch/arm/include/asm/arch-omap5/sys_proto.h b/arch/arm/include/asm/arch-omap5/sys_proto.h index ab0e7fa..d391ee5 100644 --- a/arch/arm/include/asm/arch-omap5/sys_proto.h +++ b/arch/arm/include/asm/arch-omap5/sys_proto.h @@ -35,6 +35,12 @@ struct pad_conf_entry { u32 val; };
+struct mmc_platform_fixups { + const char *hw_rev; + u32 unsupported_caps; + u32 max_freq; +}; + struct omap_sysinfo { char *board_string; }; @@ -70,6 +76,7 @@ void force_emif_self_refresh(void); void get_ioregs(const struct ctrl_ioregs **regs); void srcomp_enable(void); void setup_warmreset_time(void); +const struct mmc_platform_fixups *platform_fixups_mmc(uint32_t addr);
static inline u32 div_round_up(u32 num, u32 den) { diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index 0893844..3073805 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -70,6 +70,7 @@ struct omap_hsmmc_plat { struct mmc mmc; bool cd_inverted; u32 controller_flags; + const char *hw_rev; };
/* diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 67ba31e..a83c589 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -96,6 +96,7 @@ struct omap_hsmmc_data { struct omap_hsmmc_adma_desc *adma_desc_table; uint desc_slot; #endif + const char *hw_rev; #ifdef CONFIG_IODELAY_RECALIBRATION struct omap_hsmmc_pinctrl_state *default_pinctrl_state; struct omap_hsmmc_pinctrl_state *hs_pinctrl_state; @@ -1362,6 +1363,7 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, if ((get_cpu_family() == CPU_OMAP34XX) && (get_cpu_rev() <= CPU_3XX_ES21)) cfg->b_max = 1; #endif + mmc = mmc_create(cfg, priv); if (mmc == NULL) return -1; @@ -1581,20 +1583,28 @@ err_pinctrl_state: return 0; }
-#define OMAP_HSMMC_SETUP_PINCTRL(capmask, mode) \ - do { \ - struct omap_hsmmc_pinctrl_state *s; \ - if (!(cfg->host_caps & capmask)) \ - break; \ - \ - s = omap_hsmmc_get_pinctrl_by_mode(mmc, #mode); \ - if (!s) { \ - debug("%s: no pinctrl for %s\n", \ - mmc->dev->name, #mode); \ - cfg->host_caps &= ~(capmask); \ - } else { \ - priv->mode##_pinctrl_state = s; \ - } \ +#define OMAP_HSMMC_SETUP_PINCTRL(capmask, mode) \ + do { \ + struct omap_hsmmc_pinctrl_state *s = NULL; \ + char str[20]; \ + if (!(cfg->host_caps & capmask)) \ + break; \ + \ + if (priv->hw_rev) { \ + sprintf(str, "%s-%s", #mode, priv->hw_rev); \ + s = omap_hsmmc_get_pinctrl_by_mode(mmc, str); \ + } \ + \ + if (!s) \ + s = omap_hsmmc_get_pinctrl_by_mode(mmc, #mode); \ + \ + if (!s) { \ + debug("%s: no pinctrl for %s\n", \ + mmc->dev->name, #mode); \ + cfg->host_caps &= ~(capmask); \ + } else { \ + priv->mode##_pinctrl_state = s; \ + } \ } while (0)
static int omap_hsmmc_get_pinctrl_state(struct mmc *mmc) @@ -1629,12 +1639,22 @@ static int omap_hsmmc_get_pinctrl_state(struct mmc *mmc) #endif
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) +#ifdef CONFIG_OMAP54XX +__weak const struct mmc_platform_fixups *platform_fixups_mmc(uint32_t addr) +{ + return NULL; +} +#endif + static int omap_hsmmc_ofdata_to_platdata(struct udevice *dev) { struct omap_hsmmc_plat *plat = dev_get_platdata(dev); struct omap_mmc_of_data *of_data = (void *)dev_get_driver_data(dev);
struct mmc_config *cfg = &plat->cfg; +#ifdef CONFIG_OMAP54XX + const struct mmc_platform_fixups *fixups; +#endif const void *fdt = gd->fdt_blob; int node = dev_of_offset(dev); int ret; @@ -1658,6 +1678,15 @@ static int omap_hsmmc_ofdata_to_platdata(struct udevice *dev) if (of_data) plat->controller_flags |= of_data->controller_flags;
+#ifdef CONFIG_OMAP54XX + fixups = platform_fixups_mmc(devfdt_get_addr(dev)); + if (fixups) { + plat->hw_rev = fixups->hw_rev; + cfg->host_caps &= ~fixups->unsupported_caps; + cfg->f_max = fixups->max_freq; + } +#endif + #ifdef OMAP_HSMMC_USE_GPIO plat->cd_inverted = fdtdec_get_bool(fdt, node, "cd-inverted"); #endif @@ -1689,6 +1718,7 @@ static int omap_hsmmc_probe(struct udevice *dev) cfg->name = "OMAP SD/MMC"; priv->base_addr = plat->base_addr; priv->controller_flags = plat->controller_flags; + priv->hw_rev = plat->hw_rev; #ifdef OMAP_HSMMC_USE_GPIO priv->cd_inverted = plat->cd_inverted; #endif

The default configuration is usually working fine for the the HS modes. Don't enforce the presence of a dedicated pinmux for the HS modes.
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- drivers/mmc/omap_hsmmc.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index a83c589..615eb4c 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -328,6 +328,9 @@ static void omap_hsmmc_io_recalibrate(struct mmc *mmc) break; }
+ if (!pinctrl_state) + pinctrl_state = priv->default_pinctrl_state; + if (priv->controller_flags & OMAP_HSMMC_REQUIRE_IODELAY) { if (pinctrl_state->iodelay) late_recalibrate_iodelay(pinctrl_state->padconf, @@ -1583,7 +1586,7 @@ err_pinctrl_state: return 0; }
-#define OMAP_HSMMC_SETUP_PINCTRL(capmask, mode) \ +#define OMAP_HSMMC_SETUP_PINCTRL(capmask, mode, optional) \ do { \ struct omap_hsmmc_pinctrl_state *s = NULL; \ char str[20]; \ @@ -1598,7 +1601,7 @@ err_pinctrl_state: if (!s) \ s = omap_hsmmc_get_pinctrl_by_mode(mmc, #mode); \ \ - if (!s) { \ + if (!s && !optional) { \ debug("%s: no pinctrl for %s\n", \ mmc->dev->name, #mode); \ cfg->host_caps &= ~(capmask); \ @@ -1624,15 +1627,15 @@ static int omap_hsmmc_get_pinctrl_state(struct mmc *mmc)
priv->default_pinctrl_state = default_pinctrl;
- OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR104), sdr104); - OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR50), sdr50); - OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_DDR50), ddr50); - OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR25), sdr25); - OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR12), sdr12); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR104), sdr104, false); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR50), sdr50, false); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_DDR50), ddr50, false); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR25), sdr25, false); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR12), sdr12, false);
- OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(MMC_HS_200), hs200_1_8v); - OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(MMC_DDR_52), ddr_1_8v); - OMAP_HSMMC_SETUP_PINCTRL(MMC_MODE_HS, hs); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(MMC_HS_200), hs200_1_8v, false); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(MMC_DDR_52), ddr_1_8v, false); + OMAP_HSMMC_SETUP_PINCTRL(MMC_MODE_HS, hs, true);
return 0; }

When the clock is applied, compute the actual value of the clock. It may be slightly different from the requested value (max freq, divisor threshold)
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- drivers/mmc/omap_hsmmc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 615eb4c..82ec69e 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -1143,7 +1143,8 @@ static void omap_hsmmc_set_clock(struct mmc *mmc) } }
- priv->clock = mmc->clock; + priv->clock = MMC_CLOCK_REFERENCE * 1000000 / dsor; + mmc->clock = priv->clock; omap_hsmmc_start_clock(mmc_base); }

This callback is used to send the 74 clock cycles after power up.
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- drivers/mmc/omap_hsmmc.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 82ec69e..0a4f8a7 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -46,6 +46,7 @@ #include <asm/arch/mux.h> #endif #include <dm.h> +#include <power/regulator.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -562,6 +563,14 @@ tuning_error:
return ret; } + +static void omap_hsmmc_send_init_stream(struct udevice *dev) +{ + struct omap_hsmmc_data *priv = dev_get_priv(dev); + struct hsmmc *mmc_base = priv->base_addr; + + mmc_init_stream(mmc_base); +} #endif
static void mmc_enable_irq(struct mmc *mmc, struct mmc_cmd *cmd) @@ -650,7 +659,10 @@ static int omap_hsmmc_init_setup(struct mmc *mmc) writel(readl(&mmc_base->hctl) | SDBP_PWRON, &mmc_base->hctl);
mmc_enable_irq(mmc, NULL); + +#if !CONFIG_IS_ENABLED(DM_MMC) mmc_init_stream(mmc_base); +#endif
return 0; } @@ -1273,6 +1285,7 @@ static const struct dm_mmc_ops omap_hsmmc_ops = { .get_wp = omap_hsmmc_getwp, #endif .execute_tuning = omap_hsmmc_execute_tuning, + .send_init_stream = omap_hsmmc_send_init_stream, }; #else static const struct mmc_ops omap_hsmmc_ops = {

From: Kishon Vijay Abraham I kishon@ti.com
mmc core has defined a new parameter *clk_disable* to gate the clock. Disable the clock here if *clk_disable* is set.
Signed-off-by: Kishon Vijay Abraham I kishon@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- drivers/mmc/omap_hsmmc.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 0a4f8a7..f3da446 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -1203,6 +1203,7 @@ static int omap_hsmmc_set_ios(struct udevice *dev) struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); struct mmc *mmc = upriv->mmc; #endif + struct hsmmc *mmc_base = priv->base_addr;
if (priv->bus_width != mmc->bus_width) omap_hsmmc_set_bus_width(mmc); @@ -1210,6 +1211,11 @@ static int omap_hsmmc_set_ios(struct udevice *dev) if (priv->clock != mmc->clock) omap_hsmmc_set_clock(mmc);
+ if (mmc->clk_disable) + omap_hsmmc_stop_clock(mmc_base); + else + omap_hsmmc_start_clock(mmc_base); + #if CONFIG_IS_ENABLED(DM_MMC) if (priv->mode != mmc->selected_mode) omap_hsmmc_set_timing(mmc);

I/O data lines of UHS SD card operates at 1.8V when in UHS speed mode (same is true for eMMC in DDR and HS200 modes). Add support to switch signal voltage to 1.8V in order to support UHS cards and eMMC HS200 and DDR modes.
Signed-off-by: Kishon Vijay Abraham I kishon@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- arch/arm/include/asm/omap_mmc.h | 10 ++- drivers/mmc/omap_hsmmc.c | 162 +++++++++++++++++++++++++++++++++++----- 2 files changed, 151 insertions(+), 21 deletions(-)
diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index 3073805..6871f54 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -172,10 +172,6 @@ struct omap_hsmmc_plat { #define VS30_3V0SUP BIT(25) #define VS18_1V8SUP BIT(26)
-#define IOV_3V3 3300000 -#define IOV_3V0 3000000 -#define IOV_1V8 1800000 - #define AC12_ET (1 << 22) #define AC12_V1V8_SIGEN (1 << 19) #define AC12_SCLK_SEL (1 << 23) @@ -224,6 +220,12 @@ struct omap_hsmmc_plat { IE_DTO | IE_CIE | IE_CEB | IE_CCRC | IE_ADMAE | IE_CTO |\ IE_BRR | IE_BWR | IE_TC | IE_CC)
+#define CON_CLKEXTFREE BIT(16) +#define CON_PADEN BIT(15) +#define PSTATE_CLEV BIT(24) +#define PSTATE_DLEV (0xF << 20) +#define PSTATE_DLEV_DAT0 (0x1 << 20) + int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, int wp_gpio);
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index f3da446..ecf5171 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -78,6 +78,7 @@ struct omap_hsmmc_data { #endif uint bus_width; uint clock; + ushort last_cmd; #ifdef OMAP_HSMMC_USE_GPIO #if CONFIG_IS_ENABLED(DM_MMC) struct gpio_desc cd_gpio; /* Change Detect GPIO */ @@ -89,7 +90,6 @@ struct omap_hsmmc_data { #endif #endif #if CONFIG_IS_ENABLED(DM_MMC) - uint iov; enum bus_mode mode; #endif u8 controller_flags; @@ -98,6 +98,8 @@ struct omap_hsmmc_data { uint desc_slot; #endif const char *hw_rev; + struct udevice *pbias_supply; + uint signal_voltage; #ifdef CONFIG_IODELAY_RECALIBRATION struct omap_hsmmc_pinctrl_state *default_pinctrl_state; struct omap_hsmmc_pinctrl_state *hs_pinctrl_state; @@ -254,7 +256,8 @@ static unsigned char mmc_board_init(struct mmc *mmc) &prcm_base->iclken1_core); #endif
-#if defined(CONFIG_OMAP54XX) || defined(CONFIG_OMAP44XX) +#if (defined(CONFIG_OMAP54XX) || defined(CONFIG_OMAP44XX)) &&\ + !CONFIG_IS_ENABLED(DM_REGULATOR) /* PBIAS config needed for MMC1 only */ if (mmc_get_blk_desc(mmc)->devnum == 0) vmmc_pbias_config(LDO_VOLT_3V0); @@ -398,7 +401,7 @@ static void omap_hsmmc_set_timing(struct mmc *mmc) omap_hsmmc_start_clock(mmc_base); }
-static void omap_hsmmc_conf_bus_power(struct mmc *mmc) +static void omap_hsmmc_conf_bus_power(struct mmc *mmc, uint signal_voltage) { struct hsmmc *mmc_base; struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); @@ -408,14 +411,11 @@ static void omap_hsmmc_conf_bus_power(struct mmc *mmc)
val = readl(&mmc_base->hctl) & ~SDVS_MASK;
- switch (priv->iov) { - case IOV_3V3: - val |= SDVS_3V3; - break; - case IOV_3V0: + switch (signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: val |= SDVS_3V0; break; - case IOV_1V8: + case MMC_SIGNAL_VOLTAGE_180: val |= SDVS_1V8; break; } @@ -423,7 +423,126 @@ static void omap_hsmmc_conf_bus_power(struct mmc *mmc) writel(val, &mmc_base->hctl); }
-static void omap_hsmmc_set_capabilities(struct mmc *mmc) +static int omap_hsmmc_wait_dat0(struct udevice *dev, int state, int timeout) +{ + int ret = -ETIMEDOUT; + u32 con; + bool dat0_high; + bool target_dat0_high = !!state; + struct omap_hsmmc_data *priv = dev_get_priv(dev); + struct hsmmc *mmc_base = priv->base_addr; + + con = readl(&mmc_base->con); + writel(con | CON_CLKEXTFREE | CON_PADEN, &mmc_base->con); + + timeout = DIV_ROUND_UP(timeout, 10); /* check every 10 us. */ + while (timeout--) { + dat0_high = !!(readl(&mmc_base->pstate) & PSTATE_DLEV_DAT0); + if (dat0_high == target_dat0_high) { + ret = 0; + break; + } + udelay(10); + } + writel(con, &mmc_base->con); + + return ret; +} + +#if CONFIG_IS_ENABLED(DM_REGULATOR) +static int omap_hsmmc_set_io_regulator(struct mmc *mmc, int mV) +{ + int ret = 0; + int uV = mV * 1000; + + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + + if (!mmc->vqmmc_supply) + return 0; + + /* Disable PBIAS */ + ret = regulator_set_enable(priv->pbias_supply, false); + if (ret && ret != -ENOSYS) + return ret; + + /* Turn off IO voltage */ + ret = regulator_set_enable(mmc->vqmmc_supply, false); + if (ret && ret != -ENOSYS) + return ret; + /* Program a new IO voltage value */ + ret = regulator_set_value(mmc->vqmmc_supply, uV); + if (ret) + return ret; + /* Turn on IO voltage */ + ret = regulator_set_enable(mmc->vqmmc_supply, true); + if (ret && ret != -ENOSYS) + return ret; + + /* Program PBIAS voltage*/ + ret = regulator_set_value(priv->pbias_supply, uV); + if (ret && ret != -ENOSYS) + return ret; + /* Enable PBIAS */ + ret = regulator_set_enable(priv->pbias_supply, true); + if (ret && ret != -ENOSYS) + return ret; + + return 0; +} +#endif + +static int omap_hsmmc_set_signal_voltage(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + struct hsmmc *mmc_base = priv->base_addr; + int mv = mmc_voltage_to_mv(mmc->signal_voltage); + u32 capa_mask; + u32 ac12_v1v8; + __maybe_unused u8 palmas_ldo_volt; + u32 val; + + if (mv < 0) + return -EINVAL; + + if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_330) { + /* Use 3.0V rather than 3.3V */ + mv = 3000; + capa_mask = VS30_3V0SUP; + ac12_v1v8 = 0; + palmas_ldo_volt = LDO_VOLT_3V0; + } else if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180) { + capa_mask = VS18_1V8SUP; + ac12_v1v8 = AC12_V1V8_SIGEN; + palmas_ldo_volt = LDO_VOLT_1V8; + } else { + return -EOPNOTSUPP; + } + + val = readl(&mmc_base->capa); + if (!(val & capa_mask)) + return -EOPNOTSUPP; + + priv->signal_voltage = mmc->signal_voltage; + + omap_hsmmc_conf_bus_power(mmc, mmc->signal_voltage); + + val = readl(&mmc_base->ac12); + val = (val & ~AC12_V1V8_SIGEN) | ac12_v1v8; + writel(val, &mmc_base->ac12); + +#if CONFIG_IS_ENABLED(DM_REGULATOR) + return omap_hsmmc_set_io_regulator(mmc, mv); +#elif (defined(CONFIG_OMAP54XX) || defined(CONFIG_OMAP44XX)) && \ + defined(CONFIG_PALMAS_POWER) + if (mmc_get_blk_desc(mmc)->devnum == 0) + vmmc_pbias_config(palmas_ldo_volt); + return 0; +#else + return 0; +#endif +} + +static uint32_t omap_hsmmc_set_capabilities(struct mmc *mmc) { struct hsmmc *mmc_base; struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); @@ -434,18 +553,17 @@ static void omap_hsmmc_set_capabilities(struct mmc *mmc)
if (priv->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) { val |= (VS30_3V0SUP | VS18_1V8SUP); - priv->iov = IOV_3V0; } else if (priv->controller_flags & OMAP_HSMMC_NO_1_8_V) { val |= VS30_3V0SUP; val &= ~VS18_1V8SUP; - priv->iov = IOV_3V0; } else { val |= VS18_1V8SUP; val &= ~VS30_3V0SUP; - priv->iov = IOV_1V8; }
writel(val, &mmc_base->capa); + + return val; }
static void omap_hsmmc_disable_tuning(struct mmc *mmc) @@ -628,8 +746,9 @@ static int omap_hsmmc_init_setup(struct mmc *mmc) #endif
#if CONFIG_IS_ENABLED(DM_MMC) - omap_hsmmc_set_capabilities(mmc); - omap_hsmmc_conf_bus_power(mmc); + reg_val = omap_hsmmc_set_capabilities(mmc); + omap_hsmmc_conf_bus_power(mmc, (reg_val & VS30_3V0SUP) ? + MMC_SIGNAL_VOLTAGE_330 : MMC_SIGNAL_VOLTAGE_180); #else writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl); writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP, @@ -840,6 +959,7 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, struct hsmmc *mmc_base; unsigned int flags, mmc_stat; ulong start; + priv->last_cmd = cmd->cmdidx;
mmc_base = priv->base_addr;
@@ -1204,6 +1324,7 @@ static int omap_hsmmc_set_ios(struct udevice *dev) struct mmc *mmc = upriv->mmc; #endif struct hsmmc *mmc_base = priv->base_addr; + int ret = 0;
if (priv->bus_width != mmc->bus_width) omap_hsmmc_set_bus_width(mmc); @@ -1219,8 +1340,11 @@ static int omap_hsmmc_set_ios(struct udevice *dev) #if CONFIG_IS_ENABLED(DM_MMC) if (priv->mode != mmc->selected_mode) omap_hsmmc_set_timing(mmc); + + if (priv->signal_voltage != mmc->signal_voltage) + ret = omap_hsmmc_set_signal_voltage(mmc); #endif - return 0; + return ret; }
#ifdef OMAP_HSMMC_USE_GPIO @@ -1292,6 +1416,7 @@ static const struct dm_mmc_ops omap_hsmmc_ops = { #endif .execute_tuning = omap_hsmmc_execute_tuning, .send_init_stream = omap_hsmmc_send_init_stream, + .wait_dat0 = omap_hsmmc_wait_dat0, }; #else static const struct mmc_ops omap_hsmmc_ops = { @@ -1753,7 +1878,10 @@ static int omap_hsmmc_probe(struct udevice *dev) if (mmc == NULL) return -1; #endif - +#if CONFIG_IS_ENABLED(DM_REGULATOR) + device_get_supply_regulator(dev, "pbias-supply", + &priv->pbias_supply); +#endif #if defined(OMAP_HSMMC_USE_GPIO) && CONFIG_IS_ENABLED(OF_CONTROL) gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, GPIOD_IS_IN); gpio_request_by_name(dev, "wp-gpios", 0, &priv->wp_gpio, GPIOD_IS_IN);

From: Kishon Vijay Abraham I kishon@ti.com
Now that omap_hsmmc has support for hs200 mode, change the clock frequency to 192MHz. Also change the REFERENCE CLOCK frequency to 192MHz based on which the internal mmc clock divider is calculated.
Signed-off-by: Kishon Vijay Abraham I kishon@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- arch/arm/include/asm/arch-omap5/clock.h | 2 +- arch/arm/include/asm/omap_mmc.h | 4 ++++ arch/arm/mach-omap2/omap5/hw_data.c | 10 +++++----- 3 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/arch/arm/include/asm/arch-omap5/clock.h b/arch/arm/include/asm/arch-omap5/clock.h index ee2e78b..3d718c0 100644 --- a/arch/arm/include/asm/arch-omap5/clock.h +++ b/arch/arm/include/asm/arch-omap5/clock.h @@ -135,7 +135,7 @@
/* CM_L3INIT_HSMMCn_CLKCTRL */ #define HSMMC_CLKCTRL_CLKSEL_MASK (1 << 24) -#define HSMMC_CLKCTRL_CLKSEL_DIV_MASK (1 << 25) +#define HSMMC_CLKCTRL_CLKSEL_DIV_MASK (3 << 25)
/* CM_L3INIT_SATA_CLKCTRL */ #define SATA_CLKCTRL_OPTFCLKEN_MASK (1 << 8) diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index 6871f54..d604b79 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -199,7 +199,11 @@ struct omap_hsmmc_plat { #define MMC_CMD0 (INDEX(0) | RSP_TYPE_NONE | DP_NO_DATA | DDIR_WRITE)
/* Clock Configurations and Macros */ +#ifdef CONFIG_OMAP54XX +#define MMC_CLOCK_REFERENCE 192 /* MHz */ +#else #define MMC_CLOCK_REFERENCE 96 /* MHz */ +#endif
/* DLL */ #define DLL_SWT (1 << 20) diff --git a/arch/arm/mach-omap2/omap5/hw_data.c b/arch/arm/mach-omap2/omap5/hw_data.c index 3bdb114..30e3b68 100644 --- a/arch/arm/mach-omap2/omap5/hw_data.c +++ b/arch/arm/mach-omap2/omap5/hw_data.c @@ -438,17 +438,17 @@ void enable_basic_clocks(void) setbits_le32((*prcm)->cm_l4per_gpio4_clkctrl, GPIO4_CLKCTRL_OPTFCLKEN_MASK);
- /* Enable 96 MHz clock for MMC1 & MMC2 */ + /* Enable 192 MHz clock for MMC1 & MMC2 */ setbits_le32((*prcm)->cm_l3init_hsmmc1_clkctrl, HSMMC_CLKCTRL_CLKSEL_MASK); setbits_le32((*prcm)->cm_l3init_hsmmc2_clkctrl, HSMMC_CLKCTRL_CLKSEL_MASK);
/* Set the correct clock dividers for mmc */ - setbits_le32((*prcm)->cm_l3init_hsmmc1_clkctrl, - HSMMC_CLKCTRL_CLKSEL_DIV_MASK); - setbits_le32((*prcm)->cm_l3init_hsmmc2_clkctrl, - HSMMC_CLKCTRL_CLKSEL_DIV_MASK); + clrbits_le32((*prcm)->cm_l3init_hsmmc1_clkctrl, + HSMMC_CLKCTRL_CLKSEL_DIV_MASK); + clrbits_le32((*prcm)->cm_l3init_hsmmc2_clkctrl, + HSMMC_CLKCTRL_CLKSEL_DIV_MASK);
/* Select 32KHz clock as the source of GPTIMER1 */ setbits_le32((*prcm)->cm_wkup_gptimer1_clkctrl,

From: Kishon Vijay Abraham I kishon@ti.com
Use the new compatible string "ti,dra7-hsmmc" that was specifically added for dra7 and dra72. This is required since for dra7 and dra72 processors iodelay values has to be set unlike other processors.
Signed-off-by: Kishon Vijay Abraham I kishon@ti.com Signed-off-by: Sekhar Nori nsekhar@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- arch/arm/dts/dra7.dtsi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/arm/dts/dra7.dtsi b/arch/arm/dts/dra7.dtsi index 02a136a..9061843 100644 --- a/arch/arm/dts/dra7.dtsi +++ b/arch/arm/dts/dra7.dtsi @@ -1056,7 +1056,7 @@ };
mmc1: mmc@4809c000 { - compatible = "ti,omap4-hsmmc"; + compatible = "ti,dra7-hsmmc", "ti,omap4-hsmmc"; reg = <0x4809c000 0x400>; interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>; ti,hwmods = "mmc1"; @@ -1070,7 +1070,7 @@ };
mmc2: mmc@480b4000 { - compatible = "ti,omap4-hsmmc"; + compatible = "ti,dra7-hsmmc", "ti,omap4-hsmmc"; reg = <0x480b4000 0x400>; interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>; ti,hwmods = "mmc2"; @@ -1082,7 +1082,7 @@ };
mmc3: mmc@480ad000 { - compatible = "ti,omap4-hsmmc"; + compatible = "ti,dra7-hsmmc", "ti,omap4-hsmmc"; reg = <0x480ad000 0x400>; interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>; ti,hwmods = "mmc3"; @@ -1095,7 +1095,7 @@ };
mmc4: mmc@480d1000 { - compatible = "ti,omap4-hsmmc"; + compatible = "ti,dra7-hsmmc", "ti,omap4-hsmmc"; reg = <0x480d1000 0x400>; interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>; ti,hwmods = "mmc4";

On DRA7 family SoCs, MMC1 controller supports SDR104, SDR50, DDR50, SDR25 and SDR12 UHS modes.
MMC2 controller supports HS200 and DDR modes.
MMC3 controller supports SDR12, SDR25 and SDR50 modes.
MMC4 controller supports SDR12 and SDR25 modes.
Add these supported modes in device-tree file.
Signed-off-by: Kishon Vijay Abraham I kishon@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- arch/arm/dts/dra7.dtsi | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/arch/arm/dts/dra7.dtsi b/arch/arm/dts/dra7.dtsi index 9061843..0f982d8 100644 --- a/arch/arm/dts/dra7.dtsi +++ b/arch/arm/dts/dra7.dtsi @@ -1067,6 +1067,11 @@ status = "disabled"; pbias-supply = <&pbias_mmc_reg>; max-frequency = <192000000>; + sd-uhs-sdr104; + sd-uhs-sdr50; + sd-uhs-ddr50; + sd-uhs-sdr25; + sd-uhs-sdr12; };
mmc2: mmc@480b4000 { @@ -1079,6 +1084,10 @@ dma-names = "tx", "rx"; status = "disabled"; max-frequency = <192000000>; + sd-uhs-sdr25; + sd-uhs-sdr12; + mmc-hs200-1_8v; + mmc-ddr-1_8v; };
mmc3: mmc@480ad000 { @@ -1092,6 +1101,9 @@ status = "disabled"; /* Errata i887 limits max-frequency of MMC3 to 64 MHz */ max-frequency = <64000000>; + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; };
mmc4: mmc@480d1000 { @@ -1104,6 +1116,8 @@ dma-names = "tx", "rx"; status = "disabled"; max-frequency = <192000000>; + sd-uhs-sdr12; + sd-uhs-sdr25; };
mmu0_dsp1: mmu@40d01000 {

The UHS modes are not supported in beagle-x15 because it's not possible to switch the IO lines supply voltage to 1.8v. Also HS200 cannot be supported on mmc2, because the IO lines of mmc2 are connected to 3.3v.
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- arch/arm/dts/am57xx-beagle-x15.dts | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/arch/arm/dts/am57xx-beagle-x15.dts b/arch/arm/dts/am57xx-beagle-x15.dts index d668910..8d9bdf1 100644 --- a/arch/arm/dts/am57xx-beagle-x15.dts +++ b/arch/arm/dts/am57xx-beagle-x15.dts @@ -25,6 +25,11 @@ pinctrl-1 = <&mmc1_pins_hs>;
vmmc-supply = <&ldo1_reg>; + /delete-property/ sd-uhs-sdr104; + /delete-property/ sd-uhs-sdr50; + /delete-property/ sd-uhs-ddr50; + /delete-property/ sd-uhs-sdr25; + /delete-property/ sd-uhs-sdr12; };
&mmc2 { @@ -32,6 +37,7 @@ pinctrl-0 = <&mmc2_pins_default>; pinctrl-1 = <&mmc2_pins_hs>; pinctrl-2 = <&mmc2_pins_ddr_3_3v_rev11 &mmc2_iodelay_ddr_3_3v_rev11_conf>; + /delete-property/ mmc-hs200-1_8v; };
/* errata i880 "Ethernet RGMII2 Limited to 10/100 Mbps" */

HS200 cannot be supported on mmc2, because the IO lines of mmc2 are connected to 3.3v.
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- arch/arm/dts/am57xx-idk-common.dtsi | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/arch/arm/dts/am57xx-idk-common.dtsi b/arch/arm/dts/am57xx-idk-common.dtsi index 97aa8e6..fa5a078 100644 --- a/arch/arm/dts/am57xx-idk-common.dtsi +++ b/arch/arm/dts/am57xx-idk-common.dtsi @@ -413,6 +413,8 @@ bus-width = <8>; ti,non-removable; max-frequency = <96000000>; + no-1-8-v; + /delete-property/ mmc-hs200-1_8v; };
&dcan1 {

From: Kishon Vijay Abraham I kishon@ti.com
Since DRA7xx/AM57xx SR1.1 and SR1.0 has errata to limit the frequency of MMC1 to 96MHz and frequency of MMC2 to 48MHz for AM572x SR1.1, limit the frequency and disable higher speed modes for those revision. Also use the recommended IO delays (those tagged with "rev11")
Signed-off-by: Kishon Vijay Abraham I kishon@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- board/ti/am57xx/board.c | 30 ++++++++++++++++++++++++++++++ board/ti/dra7xx/evm.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+)
diff --git a/board/ti/am57xx/board.c b/board/ti/am57xx/board.c index f79aefd..4f2f8ef 100644 --- a/board/ti/am57xx/board.c +++ b/board/ti/am57xx/board.c @@ -30,6 +30,7 @@ #include <dwc3-uboot.h> #include <dwc3-omap-uboot.h> #include <ti-usb-phy-uboot.h> +#include <mmc.h>
#include "../common/board_detect.h" #include "mux_data.h" @@ -762,6 +763,35 @@ int board_mmc_init(bd_t *bis) omap_mmc_init(1, 0, 0, -1, -1); return 0; } + +static const struct mmc_platform_fixups am57x_es1_1_mmc1_fixups = { + .hw_rev = "rev11", + .unsupported_caps = MMC_CAP(MMC_HS_200) | + MMC_CAP(UHS_SDR104), + .max_freq = 96000000, +}; + +static const struct mmc_platform_fixups am57x_es1_1_mmc23_fixups = { + .hw_rev = "rev11", + .unsupported_caps = MMC_CAP(MMC_HS_200) | + MMC_CAP(UHS_SDR104) | + MMC_CAP(UHS_SDR50), + .max_freq = 48000000, +}; + +const struct mmc_platform_fixups *platform_fixups_mmc(uint32_t addr) +{ + switch (omap_revision()) { + case DRA752_ES1_0: + case DRA752_ES1_1: + if (addr == OMAP_HSMMC1_BASE) + return &am57x_es1_1_mmc1_fixups; + else + return &am57x_es1_1_mmc23_fixups; + default: + return NULL; + } +} #endif
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_OS_BOOT) diff --git a/board/ti/dra7xx/evm.c b/board/ti/dra7xx/evm.c index 97aae01..f82e4c0 100644 --- a/board/ti/dra7xx/evm.c +++ b/board/ti/dra7xx/evm.c @@ -846,6 +846,35 @@ void board_mmc_poweron_ldo(uint voltage) palmas_mmc1_poweron_ldo(LDO1_VOLTAGE, LDO1_CTRL, voltage); } } + +static const struct mmc_platform_fixups dra7x_es1_1_mmc1_fixups = { + .hw_rev = "rev11", + .unsupported_caps = MMC_CAP(MMC_HS_200) | + MMC_CAP(UHS_SDR104), + .max_freq = 96000000, +}; + +static const struct mmc_platform_fixups dra7x_es1_1_mmc23_fixups = { + .hw_rev = "rev11", + .unsupported_caps = MMC_CAP(MMC_HS_200) | + MMC_CAP(UHS_SDR104) | + MMC_CAP(UHS_SDR50), + .max_freq = 48000000, +}; + +const struct mmc_platform_fixups *platform_fixups_mmc(uint32_t addr) +{ + switch (omap_revision()) { + case DRA752_ES1_0: + case DRA752_ES1_1: + if (addr == OMAP_HSMMC1_BASE) + return &dra7x_es1_1_mmc1_fixups; + else + return &dra7x_es1_1_mmc23_fixups; + default: + return NULL; + } +} #endif
#ifdef CONFIG_USB_DWC3
participants (2)
-
Jean-Jacques Hiblot
-
Tom Rini